2013-08-07 11 views
12

In alcuni problemi con più moduli su una singola vista.Moduli multipli in visualizzazione MVC: ModelState applicato a tutti i moduli

Supponiamo che io ho il seguente ViewModel:

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

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

Nel mio ViewModel, voglio tutti i BankInfos da visualizzare sotto vicenda, dentro le forme separate per ciascuno.

Per raggiungere questo obiettivo, sto usando una vista parziale _EditBankInfo:

@model BankInfo 

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

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

Così come mio punto di vista attuale BankInfo:

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

scorso, qui sono le mie 2 Metodi di azione:

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

Tutto questo funziona hunky dory: la convalida funziona in modo corretto, il modello pubblicato viene riconosciuto e convalidato correttamente ... Tuttavia, quando la pagina si ricarica è quando si presenta il problema. Poiché sto utilizzando lo stesso modulo più volte, il mio ModelState verrà applicato più volte. Quindi, quando si esegue un aggiornamento su un modulo, la pagina successiva carica tutti loro avranno i valori registrati.

C'è un modo per impedire facilmente che ciò accada?

Ho provato a farlo senza le viste parziali, ma questo rovina un po 'la denominazione (sono uniche, ma il modeling del server non le riconoscerà).

Grazie per eventuali risposte.

+0

Potrebbe mostrare l'azione del controllore a cui viene inviato il modulo? Sono particolarmente interessato al modello che assume come parametro e al modello che passa alla vista. –

+0

@DarinDimitrov, li ha aggiunti. Sappi che questo è un esempio semplificato, ma l'impostazione di base dovrebbe essere lì. Inoltre, probabilmente userò una specie di scenario PRG qui. – Kippie

risposta

10

Questo è un po 'complicato. Ecco come può essere risolto. Inizia spostando il tuo partial _EditBankInfo.cshtml in un modello di editor ~/Views/Shared/EditorTemplates/BankInfo.cshtml simile a questo (notare che il nome e la posizione del modello sono importanti. Dovrebbe essere inserito all'interno di ~/Views/Shared/EditorTemplates e denominato come il nome del tipo utilizzato nella proprietà di raccolta IEnumerable<T>, che nel tuo caso è 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> 

e poi nella visualizzazione principale del sbarazzarsi del ciclo foreach e sostituirlo con una semplice chiamata al EditorFor aiutante:

@model ChangeBankAccountViewModel 

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

Ora per ogni elemento del modello di editor personalizzato della raccolta BankInfos verrà visualizzato. E contrariamente a parziale, Editor modello rispetta il contesto di navigazione e genererà il seguente 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> 

... 

Ora, poiché ogni campo ha un nome specifico non ci sarà più alcun conflitto durante la pubblicazione del modulo. Si noti il ​​campo nascosto denominato model.prefix che ho inserito esplicitamente in ogni modulo.Questo sarà utilizzato da un modello personalizzato legante per il tipo 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); 
    } 
} 

che sarà registrato nella vostra Application_Start:

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

Bene, ora siamo a posto. Sbarazzarsi di ModelState.Clear nell'azione del controller in quanto non è più necessario:

[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

Grazie mille, Darin. Fantastico esempio che mi ha insegnato alcuni trucchi (chi sapeva che EditorFor consentiva le raccolte Enumerable ??). Basta avere un piccolo problema nel capire il prefisso nel caso in cui ho bisogno di aggiungere manualmente un errore del modello al mio stato del modello, ma sono sicuro che lo capirò. – Kippie

Problemi correlati