在MVC视图多种形式:ModelState中适用于所有形式 [英] Multiple forms in MVC view: ModelState applied to all forms

查看:258
本文介绍了在MVC视图多种形式:ModelState中适用于所有形式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

运行了一些麻烦与单一视图多个表单。

Running into some trouble with multiple forms on a single view.

假设我有以下视图模型:

Suppose I have the following viewmodel:

public class ChangeBankAccountViewModel  
{  
     public IEnumerable<BankInfo> BankInfos { get; set; }  
}

public class BankInfo  
{  
    [Required]  
    public string BankAccount { get; set; }  
    public long Id { get; set; }  
}

在我的视图模型,我要显示海誓山盟下的所有BankInfos,里面每个单独的形式。

In my viewmodel, I want all BankInfos to be displayed underneath eachother, inside separate forms for each.

要做到这一点,我使用的局部视图_EditBankInfo:

To achieve this, I'm using a partial view _EditBankInfo:

@model BankInfo

@using (Html.BeginForm())
{
   @Html.HiddenFor(m => m.InvoiceStructureId)
   @Html.TextBoxFor(m => m.IBANAccount)

   <button type="submit">Update this stuff</button>
}

以及我的实际看BankInfo:

As well as my actual view BankInfo:

foreach(var info in Model.BankInfos)
{
    Html.RenderPartial("_EditBankInfo", info);
}

最后,这里是我的2动作方法:

Last, here are my 2 Action Methods:

[HttpGet]
public ActionResult BankInfo()
{
    return View(new ChangeBankAccountViewModel{BankInfos = new [] {new BankInfo...});
}
[HttpPost]
public ActionResult BankInfo(BankInfo model)
{
    if(ModelState.IsValid)
       ModelState.Clear();
    return BankInfo();
}

所有这些工作虎背熊腰,脚蹬多莉:验证工作顺利,贴模型得到认可和正确验证...
然而,当重新加载网页时问题就出现了。
因为我用同样的形式多次,我的ModelState将被应用多次。因此,一种形式上进行更新时,下一个页面加载所有的人都会有贴值。

All of this is working hunky dory: Validation works smooth, posted model gets recognized and validated correctly... However, when the page reloads is when the problem arises. Because I'm using the same form multiple times, my ModelState will be applied multiple times. So when performing an update on one form, the next page load all of them will have the posted values.

有没有办法轻易prevent这种情况发生什么办法?

Is there any way to easily prevent this from happening?

我试着做无偏的看法,但螺丝了命名位(他们是唯一的,但服务器端modelbinding将无法识别它们)。

I've tried doing it without the partial views, but that screws up the naming a bit (they're unique, but serverside modelbinding won't recognize them).

感谢您的任何答案。

推荐答案

这是一个有点棘手。下面是它如何解决。通过移动 _EditBankInfo.cshtml 部分到编辑器模板〜/查看/共享/ EditorTemplates / BankInfo.cshtml 的开始看起来是这样的(请注意,模板的名称和位置是非常重要的,它应该放在〜/查看/共享/ EditorTemplates 并命名为使用的类型化的名称内在你的的IEnumerable&LT; T&GT; 集合属性,而你的情况是 BankInfo.cshtml

This is a bit tricky. Here's how it can be solved. Start by moving your _EditBankInfo.cshtml partial into an editor template ~/Views/Shared/EditorTemplates/BankInfo.cshtml that looks like this (notice that the name and location of the template is important. It should be placed inside ~/Views/Shared/EditorTemplates and named as the name of the typed used in your IEnumerable<T> collection property, which in your case is BankInfo.cshtml):

@model BankInfo

<div>
    @using (Html.BeginForm())
    {
        <input type="hidden" name="model.prefix" value="@ViewData.TemplateInfo.HtmlFieldPrefix" />
        @Html.HiddenFor(m => m.Id)
        @Html.TextBoxFor(m => m.BankAccount)

        <button type="submit">Update this stuff</button>
    }
</div>

,然后在你的主视图摆脱的foreach 循环,用一个简单的调用 EditorFor 助手:

and then in your main view get rid of the foreach loop and replace it with a simple call to the EditorFor helper:

@model ChangeBankAccountViewModel

@Html.EditorFor(x => x.BankInfos)

现在的 BankInfos的每个元素收集自定义编辑模板将被渲染。而相反部分,编辑模板尊重航行环境和会产生以下标记:

Now for each element of the BankInfos collection custom editor template will be rendered. And contrary to the partial, the editor template respects the navigational context and will generate the following markup:

<div>
    <form action="/" method="post">    
        <input type="hidden" name="model.prefix" value="BankInfos[0]" />
        <input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="BankInfos_0__Id" name="BankInfos[0].Id" type="hidden" value="1" />
        <input data-val="true" data-val-required="The BankAccount field is required." id="BankInfos_0__BankAccount" name="BankInfos[0].BankAccount" type="text" value="account 1" />    
        <button type="submit">Update this stuff</button>
    </form>
</div>

<div>
    <form action="/" method="post">    
        <input type="hidden" name="model.prefix" value="BankInfos[1]" />
        <input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="BankInfos_1__Id" name="BankInfos[1].Id" type="hidden" value="2" />
        <input data-val="true" data-val-required="The BankAccount field is required." id="BankInfos_1__BankAccount" name="BankInfos[1].BankAccount" type="text" value="account 2" />    
        <button type="submit">Update this stuff</button>
    </form>
</div>

...

现在,因为每个领域都有一个特定的名称将不再有任何冲突发布表单时。注意名为模型隐藏字段preFIX ,我明确地放在每个表单中。这将通过对 BankInfo 类型的定制模型绑定使用:

Now since every field has a specific name there will no longer be any conflicts when posting the form. Notice the hidden field named model.prefix that I explicitly placed inside each form. This will be used by a custom model binder for the BankInfo type:

public class BankInfoModelBinder: DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        bindingContext.ModelName = controllerContext.HttpContext.Request.Form["model.prefix"];
        return base.BindModel(controllerContext, bindingContext);
    }
}

这将在注册

的Application_Start

ModelBinders.Binders.Add(typeof(BankInfo), new BankInfoModelBinder());

好了,现在我们是好去。在你的控制器动作摆脱 ModelState.Clear 因为你不再需要它:

[HttpGet]
public ActionResult BankInfo()
{
    var model = new ChangeBankAccountViewModel
    {
        // This is probably populated from some data store
        BankInfos = new [] { new BankInfo... },
    }
    return View(model);
}

[HttpPost]
public ActionResult BankInfo(BankInfo model)
{
    if(ModelState.IsValid)
    {
        // TODO: the model is valid => update its value into your data store
        // DO NOT CALL ModelState.Clear anymore.   
    }

    return BankInfo();
}

这篇关于在MVC视图多种形式:ModelState中适用于所有形式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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