0
votes

I have a thymeleaf form where i can add a subcategory to a parent category:

<div th:if="${parentCategories.size() == 0}">
        <span>There are no categories, please, add some categories!</span>
    </div>
    <div th:unless="${parentCategories.size() == 0}">
        <label for="subcategoryForm">Add a subcategory</label>
        <form id="subcategoryForm" th:action="@{/category/add-subcategory}" th:object="${category}" method="post">

            <label for="name">Category name</label>
            <input id="name" type="text" th:field="*{categoryName}"/>
            <span th:if="${#fields.hasErrors('categoryName')}" th:errors="*{categoryName}" th:errorclass="error"></span>

            <label for="selectParentCategory">Enter a parent category</label>
            <select id="selectParentCategory" th:field="*{parentCategoryName}">
                <option th:each="category : ${parentCategories}"
                        th:value="${category.categoryName}"
                        th:text="${category.categoryName}"/>
                <span th:if="${#fields.hasErrors('parentCategoryName')}" th:errors="*{parentCategoryName}" th:errorclass="error"></span>
            </select>

            <button type="submit">Add category</button>
        </form>
    </div>

I have a CategoryDTO:

@Setter
@Getter
public class CategoryDTO {

    @CategoryValidator
    private String categoryName;

    @ParentCategoryValidator
    private String parentCategoryName;

}

That @CategoryValidator is validated by CategoryValidatorImpl:

@Autowired
    private CategoryRepository categoryRepository;

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(value.isBlank()){
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("{categoryDTO.blank}").addConstraintViolation();
            return false;
        }

        Optional<Category> optionalCategory = categoryRepository.findByName(value);
        if (optionalCategory.isPresent()) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("{categoryDTO.alreadyExists}").addConstraintViolation();
            return false;
        }

        Pattern pattern = Pattern.compile("[a-zA-Z\\s]*");
        Matcher matcher = pattern.matcher(value);
        if (!matcher.matches()) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("{categoryDTO.name.invalid}").addConstraintViolation();
            return false;
        }

        return true;
    }

And a part of CategoryController:

@GetMapping("/subcategory-form")
public String getSubcategoryForm(Model model){
    List<CategoryDTO> listOfParentCategories = categoryService.getParentCategories();
    model.addAttribute("category", new CategoryDTO());
    model.addAttribute("parentCategories", listOfParentCategories);
    return "add-subcategory-form";
}

@PostMapping("/add-subcategory")
public String addSubcategory(@Valid @ModelAttribute("category") CategoryDTO categoryDTO, BindingResult bindingResult){
   

 if(bindingResult.hasErrors()){
            return "add-subcategory-form";
        }
        categoryService.add(categoryDTO);
        return "redirect:/category/subcategory-form";
    }  

In the input field when i enter a blank, the validator discovers it properly using hasErrors in the controller, but when i'm returning the form after discovering the blank and returning false, it gives this:

Whitelabel Error Page This application has no explicit mapping for /error, so you are seeing this as a fallback.

Wed Nov 18 13:45:26 EET 2020 There was an unexpected error (type=Internal Server Error, status=500). An error happened during template parsing (template: "class path resource [templates/add-subcategory-form.html]") org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/add-subcategory-form.html]") at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:241) at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100) at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666) at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098) at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072) at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:362) at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:189) at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1373) at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1118) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1057) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "parentCategories.size() == 0" (template: "add-subcategory-form" - line 13, col 10) at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393) at org.attoparser.MarkupParser.parse(MarkupParser.java:257) at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230) ... 52 more Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "parentCategories.size() == 0" (template: "add-subcategory-form" - line 13, col 10) at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:290) at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166) at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66) at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109) at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138) at org.thymeleaf.standard.expression.Expression.execute(Expression.java:125) at org.thymeleaf.standard.processor.StandardIfTagProcessor.isVisible(StandardIfTagProcessor.java:59) at org.thymeleaf.standard.processor.AbstractStandardConditionalVisibilityTagProcessor.doProcess(AbstractStandardConditionalVisibilityTagProcessor.java:61) at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95) at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633) at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314) at org.thymeleaf.engine.TemplateHandlerAdapterMarkupHandler.handleOpenElementEnd(TemplateHandlerAdapterMarkupHandler.java:304) at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler$InlineMarkupAdapterPreProcessorHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:278) at org.thymeleaf.standard.inline.OutputExpressionInlinePreProcessorHandler.handleOpenElementEnd(OutputExpressionInlinePreProcessorHandler.java:186) at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:124) at org.attoparser.HtmlElement.handleOpenElementEnd(HtmlElement.java:109) at org.attoparser.HtmlMarkupHandler.handleOpenElementEnd(HtmlMarkupHandler.java:297) at org.attoparser.MarkupEventProcessorHandler.handleOpenElementEnd(MarkupEventProcessorHandler.java:402) at org.attoparser.ParsingElementMarkupUtil.parseOpenElement(ParsingElementMarkupUtil.java:159) at org.attoparser.MarkupParser.parseBuffer(MarkupParser.java:710) at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:301) ... 54 more Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method size() on null context object at org.springframework.expression.spel.ast.MethodReference.throwIfNotNullSafe(MethodReference.java:154) at org.springframework.expression.spel.ast.MethodReference.getValueRef(MethodReference.java:83) at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:70) at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:91) at org.springframework.expression.spel.ast.OpEQ.getValueInternal(OpEQ.java:42) at org.springframework.expression.spel.ast.OpEQ.getValueInternal(OpEQ.java:32) at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:112) at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:337) at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:263) ... 75 more

The thing is, for the other form of mine where i have to add just parent categories, if i add a blank, it returns the thymeleaf and prints the error message i wrote, but i can't figure this out for this form.

1

1 Answers

0
votes

The stacktrace has the following hints on the problem:

Exception evaluating SpringEL expression: "parentCategories.size() == 0" (template: "add-subcategory-form" - line 13, col 10)

and:

Attempted to call method size() on null context object at

This means that parentCategories is not present anymore in the Thymeleaf context.

You need to re-add the parentCategories in the model again when there are errors:

@PostMapping("/add-subcategory")
public String addSubcategory(@Valid @ModelAttribute("category") CategoryDTO categoryDTO, BindingResult bindingResult) {
   
        if(bindingResult.hasErrors()){
            List<CategoryDTO> listOfParentCategories = categoryService.getParentCategories();
            model.addAttribute("parentCategories", listOfParentCategories);
            return "add-subcategory-form";
        }
        categoryService.add(categoryDTO);
        return "redirect:/category/subcategory-form";
    }