The context
I have a simple association between two entities - Category
and Email
(NtoM). I'm trying to create web interface for browsing and managing them. To browse the category and to add e-mails into that category I use controller wrapped with @RequestMapping
with category ID (UUID), so all controller actions are always taking place in context of category specified with path.
I use @ModelAttribute
to pre-load context category for entire controller scope.
The problem
This approach worked well for listing and for displaying the forms. However it fails on form submission - after debugging a little, I found out that form data overrides my category @ModelAttribute
parameter.
In my code, in method save()
the category
is not really the model attribute loaded with addCategory()
method, but is populated with form data (email
model is also populated, and that is correct).
I'm looking for the solution that will allow me to bind form data only to specific @ModelAttribute
.
I've read in Spring MVC documentation that order of arguments matters, but I ordered them accordingly to examples and still it doesn't work like 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 don't want multiple @ModelAttribute
s to come from POST, just want to distinguish somehow form model from previously generated attribute(s).