Spring MVC: having multiple @ModelAttribute attributes in a form processing action

Context

I have a simple association between two objects - Category and Email (NtoM). I am trying to create a web interface for viewing and managing them. To browse a category and add emails to this category, I use a controller wrapped in @RequestMapping with a category identifier (UUID), so all controller actions always occur in the context of the category specified by the path.

I use @ModelAttribute to preload the context category for the entire control area.

Problem

This approach is well established for displaying and displaying forms. However, this fails when submitting the form - after a bit of debugging, I found that the form data overrides the parameter of my @ModelAttribute category.

In my code in the save() method, the Category is actually not a model attribute loaded with the addCategory() method, but it is populated with form data (the t28> model is also populated, and this is correct).

I am looking for a solution that will allow me to bind form data only to a specific @ModelAttribute .

I read in the Spring MVC documentation that the order of the arguments matters, but I ordered them according to the examples, and yet it doesn't work as expected.

The code

Here is my controller:

 @Controller @RequestMapping("/emails/{categoryId}") public class EmailsController { @ModelAttribute("category") public Category addCategory(@PathVariable UUID categoryId) { return this.categoryService.getCategory(categoryId); } @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Set.class, "categories", new CategoriesSetEditor(this.categoryService)); } @RequestMapping(value = "/create", method = RequestMethod.GET) public String createForm(@ModelAttribute Category category, Model model) { // here everything works, as there is just a single @ModelAttribute return "emails/form"; } @RequestMapping(value = "/save", method = RequestMethod.POST) public String save( @ModelAttribute @Valid Email email, BindingResult result, Model model, @ModelAttribute("category") Category category ) { // saving entity, etc // HERE! problem is, that response is bound BOTH to `email' and `category' model attributes // and overrides category loaded in `addCategory()' method return String.format("redirect:/emails/%s/", category.getId().toString()); } } 

Just in case, here is also the form code:

 <form:form action="${pageContext.request.contextPath}/emails/${category.id}/save" method="post" modelAttribute="email"> <form:hidden path="id"/> <fieldset> <label for="emailName"><spring:message code="email.form.label.Name" text="E-mail address"/>:</label> <form:input path="name" id="emailName" required="required"/> <form:errors path="name" cssClass="error"/> <label for="emailRealName"><spring:message code="email.form.label.RealName" text="Recipient display name"/>:</label> <form:input path="realName" id="emailRealName"/> <form:errors path="realName" cssClass="error"/> <label for="emailIsActive"><spring:message code="email.form.label.IsActive" text="Activation status"/>:</label> <form:checkbox path="active" id="emailIsActive"/> <form:errors path="active" cssClass="error"/> <form:checkboxes path="categories" element="div" items="${categories}" itemValue="id" itemLabel="name"/> <form:errors path="categories" cssClass="error"/> <button type="submit"><spring:message code="_common.form.Submit" text="Save"/></button> </fieldset> </form:form> 

Note: I do not want several @ModelAttribute to come from POST, just want to distinguish somehow the form of the model from the previously generated attributes.

+7
java spring spring-annotations spring-mvc annotations
source share
1 answer

I'm not sure I fully understand the problem, but for me, it seems you want the category object to be present in the model when the form is displayed, but you do not want it to be changed using the form message?

When you specify @ModelAttribute ("categories") in the argument list, you basically tell spring MVC to bind the form data to the annotated object using the name of the "category" parameter.

If you do not want the object to be attached, just leave it in the parameter list. If you need the source object in the handler method, manually extract it by calling addCategory and providing the identifier associated with @PathVariable:

 @RequestMapping(value = "/save", method = RequestMethod.POST) public String save( @ModelAttribute @Valid Email email, BindingResult result, Model model, @PathVaribale("categoryId") UUID categoryId ) { // saving entity, etc return String.format("redirect:/emails/%s/", categoryId.toString()); //if category object is needed and not just id then fetch it with Category c = addCategory(categoryId). } 

(PS. If you register a converter that converts Long to a category using categoryService, you can also put an @PathVariable("categoryId") Category category to map the Category object to a path variable instead of a UUID if you want it to look like 7.5. 5 Conversion Service Configuration )

(EDIT: delete the sentence to name the model in different ways, as this will not help, as indicated in the comments, and added an example)

Personally, if I needed this behavior (an object that should be present on the form when the form is displayed, but not attached to it when the form is submitted), I would not use the annotated ModelAttribute method to populate the model. Instead, I will fill out the model manually when the form is displayed. This is a bit more code (well, actually, one line), but less magical and understandable.

+3
source share

All Articles