Asp.net-Mvc

MVC 視圖中的多個表單:ModelState 應用於所有表單

  • June 13, 2014

在單個視圖上使用多個表單時遇到一些麻煩。

假設我有以下視圖模型:

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

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

在我的視圖模型中,我希望所有 BankInfos 都顯示在彼此下方,在每個單獨的表單中。

為了實現這一點,我使用了部分視圖 _EditBankInfo:

@model BankInfo

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

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

以及我的實際視圖BankInfo:

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

最後,這是我的 2 種操作方法:

[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 將被應用多次。因此,當對一個表單執行更新時,下一頁載入所有表單都將具有發布的值。

有什麼方法可以輕鬆防止這種情況發生嗎?

我試過在沒有部分視圖的情況下這樣做,但這有點搞砸了命名(它們是獨一無二的,但伺服器端模型綁定不會辨識它們)。

感謝您的任何回答。

這有點棘手。這是如何解決的。首先將您的_EditBankInfo.cshtml部分移動到一個看起來像這樣的編輯器模板~/Views/Shared/EditorTemplates/BankInfo.cshtml(注意模板的名稱和位置很重要。它應該放在裡面~/Views/Shared/EditorTemplates並命名為您的IEnumerable<T>集合屬性中使用的類型的名稱,在您的情況下是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

@model ChangeBankAccountViewModel

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

BankInfos現在將呈現集合自定義編輯器模板的每個元素。與部分相反,編輯器模板尊重導航上下文並將生成以下標記:

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

...

現在,由於每個欄位都有一個特定的名稱,因此在發布表單時將不再有任何衝突。model.prefix請注意我明確放置在每個表單中的隱藏欄位。這將被自定義模型綁定器用於該BankInfo類型:

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當您不再需要它時,去掉in 您的控制器操作:

[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();
}

引用自:https://stackoverflow.com/questions/18102918