Spring MVC:在表单处理操作中有多个@ModelAttribute [英] Spring MVC: Having multiple @ModelAttribute in form handling action

查看:41
本文介绍了Spring MVC:在表单处理操作中有多个@ModelAttribute的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个实体之间的简单关联 - CategoryEmail (NtoM).我正在尝试创建用于浏览和管理它们的 Web 界面.为了浏览类别并将电子邮件添加到该类别中,我使用带有类别 ID (UUID) 的 @RequestMapping 包裹的控制器,因此所有控制器操作总是在用路径指定的类别的上下文中发生.

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.

我使用 @ModelAttribute 为整个控制器范围预加载上下文类别.

I use @ModelAttribute to pre-load context category for entire controller scope.

这种方法适用于列出和显示表单.但是它在表单提交时失败 - 经过一些调试,我发现表单数据覆盖了我的类别 @ModelAttribute 参数.

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.

在我的代码中,在方法 save() 中,category 并不是用 addCategory() 方法加载的模型属性,而是填充表单数据(email 模型也被填充,这是正确的).

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).

我正在寻找允许我仅将表单数据绑定到特定 @ModelAttribute 的解决方案.

I'm looking for the solution that will allow me to bind form data only to specific @ModelAttribute.

我在 Spring MVC 文档中读到参数的顺序很重要,但是我根据示例对它们进行了相应的排序,但它仍然没有按预期工作.

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.

这是我的控制器:

@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>

注意:我不希望多个 @ModelAttribute 来自 POST,只是想以某种方式将表单模型与之前生成的属性区分开来.>

Note: I don't want multiple @ModelAttributes to come from POST, just want to distinguish somehow form model from previously generated attribute(s).

推荐答案

我不确定我是否完全理解这个问题,但对我来说,当您显示表单时,您似乎希望模型中出现类别对象,但不要不希望它与表单帖子一起更改?

I'm not sure I understand the problem entirely, but to me, it seems you want category object present in the model when you display the form, but don't want it to be changed with form post?

当您在参数列表中指定 @ModelAttribute("categories") 时,您基本上是告诉 Spring MVC 使用参数名称categories"将表单数据绑定到带注释的对象.

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

如果您不想绑定对象,只需将其从参数列表中删除即可.如果您需要处理程序方法中的原始对象,请通过调用 addCategory 并提供用 @PathVariable 映射的 id 手动获取它:

If you don't want the object to be bound just leave it out from the parameters list. If you need the original object in the handler method fetch it manually by calling addCategory and providing id mapped 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.如果你注册了一个使用 categoryService 将 Long 转换为 Category 的转换器,你还可以将 @PathVariable("categoryId") Category category 映射到 Category 对象到路径变量而不是 UUID,如果你想看看 7.5.5 配置转换服务)

(PS. If you register a converter that converts Long to Category using categoryService you can also put @PathVariable("categoryId") Category category to map Category object to path variable instead of UUID, if you'd like that take look at 7.5.5 Configuring a ConversionService)

(删除了以不同方式命名模型的建议,因为这在评论中没有帮助,并添加了示例)

( removed suggestion to name model differently as that will not help as noted in comments, and added example)

就我个人而言,如果我需要这种行为(显示表单时需要存在于表单中的对象,但在发布表单时不绑定到它),我不会使用 ModelAttribute 注释方法来填充模型.相反,我会在显示表单时手动填充模型.那是更多的代码(好吧,实际上只有一行)但不那么神奇且更容易理解.

Personally, if I needed this kind of behavior (an object that needs to be present in the form when displaying the form, but not bound to it when form is posted) I would not use ModelAttribute annotated method to populate the model. Instead, I'd populate the model manually when displaying the form. That is a bit more code (well, one line actually) but is less magical and easier to understand.

这篇关于Spring MVC:在表单处理操作中有多个@ModelAttribute的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆