Java – spring MVC: there are multiple @ modelattributes in the form processing operation
context
I have a simple association between two entities - category and email (ntom) I'm trying to create a web interface for browsing and managing them To browse a category and add e-mail to it, I use a controller wrapped in @ requestmapping with a category ID (UUID), so all controller operations are always performed in the context of the category specified with path
I use @ modelattribute to preload context categories for the entire controller scope
problem
This method applies to lists and display forms But it failed when the form was submitted - after a little debugging, I found that the form data overwrites my category @ modelattribute parameter
In my code, in the method save (), the category is not actually the model attribute loaded using the addcategory () method, but filled with form data (the e-mail model has also been filled, which is correct)
I am looking for a solution that can bind form data only to a specific @ modelattribute
I read in the spring MVC documentation that the order of parameters is important, but I sorted them according to the example, but it still doesn't work as expected
code
This 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:check@R_136_2419@ path="active" id="emailIsActive"/> <form:errors path="active" cssClass="error"/> <form:check@R_136_2419@es 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 don't want multiple @ modeltattributes from post, but I just want to distinguish the form model from the previously generated attributes in some way
Solution
I'm not sure if I fully understand this problem, but for me, when you display the form, you seem to want the category object to appear in the model, but don't want it to publish changes through the form?
When @ modelattribute ("categories") is specified in the parameter list, spring MVC is basically told to use the parameter name "categories" to bind form data to annotated objects
If you do not want to bind an object, remove it from the parameter list If you need the original object in the handler method, get it manually by calling addcategory and providing the ID mapped to @ pathvariable:
@RequestMapping(value = "/save",method = RequestMethod.POST) public String save( @modelattribute @Valid Email email,@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 category using categoryservice, you can also map the @ pathvariable ("categoryid") category to the path variable instead of UUID. If you like, please see 7.5 5 Configuring a ConversionService)
(edit: delete the suggestion to name the model in a different way, as this will not help, as described in the comment, and an example is added)
Personally, if I need this behavior (I need objects that appear in the form when the form is displayed, but I don't bind them when the form is published), I won't use the annotated method of modelattribute to populate the model Instead, I manually populate the model when the form is displayed This is more code (actually one line), but less magical and easier to understand