Question

I'm migrating a Spring jsp application to Thymeleaf but having problems displaying form errors.

I'm using the SpringTemplateEngine and ThymeleafViewResolver and rendering of templates works. Also form values are populated in form input fields.

The only thing so far not working is displaying form error messages.

My controller looks like:

@RequestMapping(method = RequestMethod.POST)
String save(@Valid CustomerForm form, BindingResult bindingResult, Model model, RedirectAttributes redirectAttributes) {
    if (bindingResult.hasErrors()) {
        model.addAttribute("form", form)
        return "app/customers/create"
    }
    ....

I printed the bindingResult to verify it contains an error:

binding result = org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'customerForm' on field 'name': rejected value []; codes [customerForm.name.NotBlank,name.NotBlank,java.lang.String.NotBlank,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [customerForm.name,name]; arguments []; default message [name]]; default message [may not be empty]

When I try to display error using:

<ul>
    <li th:each="e : ${#fields.detailedErrors()}" th:class="${e.global}? globalerr : fielderr">
        <span th:text="${e.global}? '*' : ${e.fieldName}">The field name</span> |
        <span th:text="${e.message}">The error message</span>
    </li>
</ul>

it does not display any error.

I tried various alternatives as documented on http://www.thymeleaf.org/doc/html/Thymeleaf-Spring3.html#validation-and-error-messages but without success.

Am I missing something?

EDIT

Note I'm trying to display the error within a form set via th:object:

<form id="customer-form" action="#" th:action="@{/app/customers}" th:object="${form}" method="post" class="form-horizontal">
    <ul>
        <li th:each="e : ${#fields.detailedErrors()}" th:class="${e.global}? globalerr : fielderr">
            <span th:text="${e.global}? '*' : ${e.fieldName}">The field name</span> |
            <span th:text="${e.message}">The error message</span>
        </li>
    </ul>
</form>
Was it helpful?

Solution

I think you may be having the same issue as I did - please see :

There it is answered by Daniel Fernandez. Basically your form object th:object="${form}" is named "form" but your controller is looking for "customerForm" (class name) not "form" (the variable name)

can be renamed with @ModelAttribute("data")

copied from that link use:

public String post(@Valid FormData formData, BindingResult result, Model model){
    // th:object="${formData}"
}

or

public String post(@Valid @ModelAttribute("data") FormData data, BindingResult result, Model model){
    // th:object="${data}"
} 

OTHER TIPS

This is how I do it in my forms:

For displaying all errors I put this at the beginning of my form:

<div class="alert alert-danger" th:if="${#fields.hasErrors('*')}">
    <p th:each="err : ${#fields.errors('*')}" th:text="${err}"></p>    
</div>

and for individual error I add this after the field (of course, changing field in hasErrors to correspond to the field tested):

<p th:if="${#fields.hasErrors('vehicle.licensePlate')}" class="label label-danger" th:errors="*{vehicle.licensePlate}">Incorrect LP</p>

Let me know if this works for you?

Adding to @Blejzer answer: naming of error message inside messages.properties file must follow below naming convention, so message string will be returned instead of message key:

(Constraint Name).(Object Name).(Property Name)

note: Object Name not Class Name

For example, if you have below User class:

class User{
    @NotBlank
    String username;

    @Length(min=6)
    String password;
}

suppose in controller, we named object under validation "user", see @ModelAttribute("user"):

@RequestMapping(value = "/signup", method = RequestMethod.POST)
public String signup(@Valid @ModelAttribute("user") User user, BindingResult bindingResult, Model model) {

    if (bindingResult.hasErrors()) {
        return "signup";
    }

    //do some processing

    return "redirect:/index";
}

in above controller snippet, the object name is "user" not "User"

Now, to show custom messages, you have to name the message as below:

NotBlank.user.username=User Name is blank
Length.user.password=Password length must be at least 6 letters
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top