2013-08-07 43 views
12

運行了一些麻煩與一個單一的視圖多種形式。多種形式在MVC視圖:ModelState中適用於所有形式的

假設我有以下視圖模型:

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將多次應用。因此,在一個表單上執行更新時,下一頁加載所有這些表單都將具有發佈的值。

有沒有辦法輕易地防止這種情況的發生?

我試着這樣做沒有局部的意見,但螺絲了命名位(他們是唯一的,但服務器端modelbinding將無法識別它們)。

感謝您的任何答案。

+0

您能否顯示錶單提交到的控制器操作?我特別感興趣的是它作爲參數傳遞給模型的模型。 –

+0

@DarinDimitrov,新增了他們。知道這是一個簡單的例子,但基本的設置應該在那裏。另外,我可能會在這裏使用某種PRG場景。 – Kippie

回答

10

這有點棘手。這是如何解決的。通過移動_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,因爲您不再需要它:

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

非常感謝Darin。令人敬畏的例子教會了我一些技巧(誰知道EditorFor允許的Enumerable集合??)。只是在計算前綴時遇到小問題,以免我需要手動向模型狀態添加模型錯誤,但我相信我會弄清楚這一點。 – Kippie