user2008973
user2008973

Reputation: 445

Spring MVC validation errors are not displayed in Freemarker template

I'm trying to display validation errors in a 'user registration' page built with freemarker template if a controller returns binding errors.

My controller's code is as follows:

@Controller
@RequestMapping("/")
public class UserController  {

@Autowired
private UserService userService;

@Autowired
private SecurityService securityService;

@Autowired
private UserValidator userValidator;

@RequestMapping(value = "/registration", method = RequestMethod.GET)
   public String registration(Model model) {
   model.addAttribute("userForm", new User()); 
   return "registration";
  }

@RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(@ModelAttribute("useraccount") User userForm,     BindingResult bindingResult, Model model) {

    userValidator.validate(userForm, bindingResult);

    if (bindingResult.hasErrors()) {

        return "registration";
        }

    userService.save(userForm);

    securityService.autologin(userForm.getUsername(), userForm.getPasswordConfirm());

    return "redirect:/explore";
}

while this is the registration.ftl freemarker template I am trying to build :

 <div>
<fieldset>
    <h1>Create your Account</h1>
    <form id="regForm" class="idealform" action="registration" method="post" name='useraccount'>
            Username: <input type="text" name="username" /> <errors path="username" cssClass="error"/><br/>
            Password: <input type="text" name="password" /><errors path="password" cssClass="error"/><br/>
               <label class="main-label" style="width: 91px;">&nbsp;</label>
        <input type="submit" value="submit">
    </form>
</fieldset>

I tried also the solution recommended here:

Displaying Spring MVC validation errors in Freemarker templates

and the registration.ftl becomes:

  <#assign form=JspTaglibs["http://www.springframework.org/tags/form"] />

  <#macro formErrors>
  <#assign formErrors><@form.errors path="*" /></#assign>
  <#if formErrors?has_content>
    <div id="errors">
     <@spring.message "admin.error.globalMessage" />
    </div>
  </#if>
 </#macro>

<div>
<fieldset>
    <h1>Create your Account</h1>
    <@form.form id="regForm" class="idealform" action="registration"   method="post" name='useraccount'>
            Username: <input type="text" name="username" path="username" />    <br/>

            Password: <input type="text" name="password" path="password" /><br/>

    <@formErrors />
        <label class="main-label" style="width: 91px;">&nbsp;</label>
        <input type="submit" value="submit">
    </@form.form>
</fieldset>

</div>

but still the validation messages are not displayed.

Could you share your thoughts with me on this issue? Thank you very much.

Upvotes: 1

Views: 2342

Answers (1)

Igor B
Igor B

Reputation: 31

I rewrote your controller code to something like this:

@Controller
@RequestMapping("/")
public class UserController {

@Autowired
private UserService userService;

@Autowired
private SecurityService securityService;

@Autowired
private UserValidator userValidator;

@RequestMapping(value = "/registration", method = RequestMethod.GET)
public String registration(@ModelAttribute(name = "userForm") User user) {
    return "registration";
}

@RequestMapping(value = "/registration", method = RequestMethod.POST)
public String registration(@ModelAttribute(name = "userForm") User user, BindingResult result) {
    userValidator.validate(user, result);

    if (result.hasErrors()) {
        return "registration";
    }

    userService.save(user);

    securityService.autologin(user.getUsername(), user.getPasswordConfirm());

    return "redirect:/explore";
}
}

1) There is no need to use model.addAttribute("modelName", model) if you use a constructor without arguments, instead you can use a @ModelAttribute annotation specifying the name attribute (by default the name goes from the class name). You only have to be sure that this model is in a consistent state. Also you need to pass the name exactly the same as you use in your view (freemarker template).

Now "registration.ftl"

...
<#import "/spring.ftl" as spring/>
...
<fieldset>
  <h1>Create your Account</h1>
  <form id="regForm" class="idealform" action="<@spring.url '/registration'/>" method="post">
    <@spring.bind 'userForm.username'/>
    Username: <input type="text" name="${spring.status.expression}" value="${spring.status.value?html}"/>
    <#list spring.status.errorMessages as error>
      <span class="error">${error}</span>
      <br>
    </#list>
    <@spring.bind 'userForm.password'/>
    Password: <input type="password" name="${spring.status.expression}" value="${spring.status.value?html}"/>
    <#list spring.status.errorMessages as error>
      <span class="error">${error}</span>
      <br>
    </#list>
    <label class="main-label" style="width: 91px;">&nbsp;</label>
    <input type="submit" value="submit">
  </form>
</fieldset>
...

1)You need <#import "/spring.ftl" as spring/> in order to add spring's user-defined directives that are pretty useful.
2)Use <@spring.bind 'userForm.username'> directive to bind following input to your model. Here "userForm" is your model and "username" is a field that you want to bind. This directive also declares new variable "spring.status" that contains an "expression" variable - for a path, a "value" - to populate the form in case it's returned with errors, and "errorMessages" from BindingResult.
3)If you use a message source to support different languages you should change <span class="error">${error}</span> to something like <@spring.message '${error}'/> otherwise you'll get just message codes.

Hope it helps.

Upvotes: 1

Related Questions