2013-08-07 19 views
12

Probleme mit mehreren Formularen in einer einzigen Ansicht.Mehrere Formulare in der MVC-Ansicht: ModelState für alle Formulare

Angenommen, ich habe folgendes Viewmodel:

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

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

In meinem Viewmodel, möchte ich alle BankInfos unter ihnen angezeigt werden, innerhalb getrennter Formen für jeden.

Um dies zu erreichen, ich bin eine Teilansicht _EditBankInfo mit:

@model BankInfo 

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

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

sowie meine aktuelle Ansicht BankInfo:

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

Last, hier sind meine 2 Action Methoden:

All dies funktioniert hunky dory: Validierung funktioniert reibungslos, Posted-Modell wird erkannt und validiert richtig ... Allerdings, wenn die Seite neu geladen wird, tritt das Problem auf. Da ich dasselbe Formular mehrmals verwende, wird mein ModelState mehrmals angewendet. Wenn Sie also ein Update auf einem Formular durchführen, werden auf der nächsten Seite alle gebuchten Werte angezeigt.

Gibt es eine Möglichkeit, dies einfach zu verhindern?

Ich habe versucht, es ohne die Teilansichten zu tun, aber das vermasselt die Benennung ein wenig (sie sind einzigartig, aber serverseitige Modelbinding wird sie nicht erkennen).

Danke für alle Antworten.

+0

Könnten Sie die Controller-Aktion zeigen, an der das Formular gesendet wird? Ich interessiere mich besonders für das Modell, das es als Parameter nimmt, und das Modell, das es an die Ansicht weitergibt. –

+0

@DarinDimitrov, fügte sie hinzu. Wissen Sie, dies ist ein vereinfachtes Beispiel, aber die grundlegende Konfiguration sollte da sein. Außerdem würde ich hier wahrscheinlich ein PRG-Szenario verwenden. – Kippie

Antwort

10

Dies ist ein bisschen schwierig. Hier ist, wie es gelöst werden kann. Beginnen Sie, indem Sie Ihre _EditBankInfo.cshtml teilweise in eine Editorvorlage ~/Views/Shared/EditorTemplates/BankInfo.cshtml verschieben, die so aussieht (beachten Sie, dass der Name und der Speicherort der Vorlage wichtig sind. Sie sollte innerhalb ~/Views/Shared/EditorTemplates platziert und als der Name der in Ihrer IEnumerable<T> Collection-Eigenschaft verwendeten Namen genannt werden in Ihrem Fall ist 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> 

und dann in der Hauptansicht loswerden der foreach Schleife bekommen und es mit einem einfachen Aufruf der EditorFor Helfer ersetzen:

@model ChangeBankAccountViewModel 

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

Jetzt Für jedes Element der BankInfos Sammlung wird eine benutzerdefinierte Editorvorlage gerendert. Und im Gegensatz zu dem teilweise respektiert die Editor-Vorlage den Navigationskontext und das folgende Markup generieren:

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

... 

Nun, da jedes Feld einen bestimmten Namen hat, wird es nicht mehr Konflikte, wenn Sie das Formular veröffentlichen. Beachten Sie das ausgeblendete Feld mit dem Namen model.prefix, das ich explizit in jedes Formular eingefügt habe.Dies wird von einem benutzerdefinierten Modell Binder für den BankInfo Typen verwendet werden:

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

, die in ihrem Application_Start registriert werden:

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

Okay, jetzt sind wir gut zu gehen. Lassen Sie sich von den ModelState.Clear in Ihrer Controller-Aktion zu befreien, wie Sie nicht mehr benötigen:

[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

Vielen Dank, Darin. Tolles Beispiel, das mir ein paar Tricks beigebracht hat (wer wusste das EditorFür erlaubte Enumerable-Sammlungen?). Habe nur ein kleines Problem beim Herausfinden des Präfixes für den Fall, dass ich einen Modellfehler manuell zu meinem Modellstatus hinzufügen muss, aber ich bin mir sicher, dass ich das herausfinden werde. – Kippie