2010-10-17 5 views
8

Ich versuche, eine Liste zu binden, die Teil eines größeren Ansichtsmodells ist, ohne auf einen benutzerdefinierten Modellbinder zurückzugreifen. Wenn ich eine Editorvorlage verwende, um die Liste der Eingaben zu erstellen, haben die generierten Namen nicht das richtige Format, damit der Standardordner funktioniert.ASP.NET MVC Model Binding IList in einem Editor Template

Anstelle von Items [3] .Id wie ich würde erwarten, dass es Items ist. [3] .Id. Wenn ich die Liste ohne eine Editor-Vorlage erstelle, funktioniert es wie erwartet.

Mache ich etwas offensichtlich falsch oder ist das nur eine Eigenart von Html.Hidden und Html.TextBox?

public class ItemWrapper 
{ 
    [UIHint("ItemList")] 
    public IList<Item> Items { get; set; } 
} 

public class Item 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
    public int Value { get; set; } 
} 

Index.aspx

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> 

    <h2>Index</h2> 

    <% using(Html.BeginForm()) 
    {%> 
    <%:Html.EditorFor(m => m.Items) %> 
    <%}%> 
</asp:Content> 

ItemList.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IList<Mvc2Test.Models.Item>>" %> 

<h4>Asset Class Allocation</h4> 
<% if(Model.Count > 0) { %> 
<table> 
    <tbody> 
    <% for(int i = 0; i < Model.Count; i++) 
    {%> 
     <tr> 
     <td><%: Model[i].Name%></td> 
     <td> 
      <%: Html.HiddenFor(m => m[i].Id) %> 
      <%: Html.TextBoxFor(m => m[i].Value) %> 
     </td> 
     </tr> 
    <%}%> 
    </tbody> 
</table> 
<% 
}%> 

Ausgabe

<tr> 
    <td>Item 4</td> 
    <td> 
    <input id="Items__3__Id" name="Items.[3].Id" type="hidden" value="f52a1f57-fca8-4bc5-a746-ee0cef4e05c2" /> 
    <input id="Items__3__Value" name="Items.[3].Value" type="text" value="40" /> 
    </td> 
</tr> 

bearbeiten (Aktion Methode)

public ActionResult Test() 
{ 
    return View(
    new ItemWrapper 
    { 
     Items = new List<Item> 
     { 
     { new Item { Id = Guid.NewGuid(), Name = "Item 1", Value = 10 } }, 
     { new Item { Id = Guid.NewGuid(), Name = "Item 2", Value = 20 } }, 
     { new Item { Id = Guid.NewGuid(), Name = "Item 3", Value = 30 } }, 
     { new Item { Id = Guid.NewGuid(), Name = "Item 4", Value = 40 } } 
     } 
    }); 
} 

Edit # 2

Httppost Aktion

[HttpPost] 
public ActionResult Test(ItemWrapper w) 
{ 
    if(w.Items == null) 
     Response.Write("Items was null"); 
    else 
     Response.Write("Items found " + w.Items.Count.ToString()); 
    return null; 
} 

Index.aspx

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> 

<h4>Does Not Work</h4> 
<% using(Html.BeginForm("Test", "Home")) 
{%> 
     <%:Html.EditorFor(m => m.Items) %> 
     <input type="submit" value-"Go" /> 
<%}%> 

<h4>Does Work</h4> 
     <% using(Html.BeginForm("Test", "Home")) 
     {%> 
    <table> 
     <tbody> 
      <% for(int i = 0; i < Model.Items.Count; i++) 
      {%> 
      <tr> 
       <td><%: Model.Items[i].Name%></td> 
       <td> 
        <%: Html.HiddenFor(m => Model.Items[i].Id) %> 
        <%: Html.TextBoxFor(m => Model.Items[i].Value) %> 
       </td> 
      </tr> 
      <%}%> 
     </tbody> 
    </table> 
      <input type="submit" value-"Go" /> 
     <%}%> 

</asp:Content> 

Antwort

7

Ich habe Ihr Problem verstanden, und ich könnte sehr gut eine Lösung zu :)!

Lassen Sie mich zuerst erklären, was ich gelernt habe, indem ich die framework's source code untersucht habe (es ist immer eine gute Idee, den Quellcode eines OpenSource-Projekts einzusehen, um besser zu verstehen, wie bestimmte Dinge funktionieren).

1-) Wenn einfach mit stark typisierte html Helfer (dh alle Html.xxxFor (...) Methoden außer EditorFor und DisplayFor), in dem Lambda-Ausdruck des Modells Eigenschaft definieren der Name des hTML-Elements zu machen, erzeugt wird, ist gleich, was auch immer Zeichenfolge folgt „Modell =>“, minus was kommt vor „=>“, das heißt:

  • die Zeichenfolge „Modell“, wenn das Modell ist eine Sammlung
  • oder die Zeichenfolge "Modell. "(beachten Sie die" . "am Ende) andernfalls.

zum Beispiel also diese:

<%: Html.TextBoxFor(m=>m.OneProperty.OneNestedProperty)%> 

wird diese HTML-Ausgabe erzeugen:

<input type="text" name="OneProperty.OneNestedProperty" ../> 

Und:

<%: Html.TextBoxFor(m=>m[0].OneProperty.OneNestedProperty)%> 

generiert dieses:

<input type="text" name="[0].OneProperty.OneNestedProperty" ../> 

==> Dies erklärt zum Teil, warum Sie diese „seltsame“ HTML-Ausgabe haben, wenn EditorFor verwenden.

2-) Bei der Verwendung von Komplex stark typisierten Helfer (EditorFor und DisplayFor), die gleiche vorherige Regel innerhalb der zugehörigen Teilansicht angelegt (ItemList.ascx in Ihrem Fall), und in Zusätzlich alle generierten hTML-Elemente werden durch das Präfix sein, was kommt nach "==>", wie in 1-) erläutert.

Das Präfix ist hier "Artikel", weil Sie diese in Ihrer typisierten Sicht haben (Index.aspx):

<%:Html.EditorFor(m => m.Items) %> 

==> Dies erklärt vollständig die Ausgabe, und warum Standard Bindemittel funktioniert nicht mehr mit der Liste der Elemente

Die Lösung auf Pause wird Ihre ItemWrapper Parameter in der [Httppost] Methode, in seinen Eigenschaften und dann verwenden, um die Bind Attribut mit seinem Präfix Parameter für jede komplexe Eigenschaft, wie folgt aus:

[HttpPost] 
    public string Index(string foo,[Bind(Prefix = "Items.")]IList<Item> items) 
    { 
     return "Hello"; 
    } 

(vorausgesetzt, daß ItemWrapper auch aufweist eine einfache Eigenschaft Foo vom Typ String genannt)

Konflikt zu vermeiden, wenn sie in der Post-Methode, die Eigenschaften Auflistung, empfehle ich Ihnen dringend zu ea nach Ihren Parametern zu nennen ch Name der Eigenschaft (no mather the case) wie ich es tat.

Hoffe, das wird helfen!

+0

Es ist also wirklich eine Marotte in der Art und Weise MVC die Feldnamen erzeugt. Die Teilansicht berücksichtigt nicht, dass das Modell eine Sammlung ist, wenn der Feldname generiert wird. Ich denke, wenn die Items.wird auf der Ansichtsebene statt auf der Ebene der Teilansicht erstellt, und es ist möglicherweise kein guter Weg, sie zu beheben. Vielen Dank. –

+0

kondotine: klingt wie ein asp.net mvc bug, hat jemand es noch gemeldet? – Wout

+0

Ok, melde mich selbst: http://aspnet.codeplex.com/workitem/7711, bitte stimme für diesen Bugfix! – Wout

-1

Eine faule Lösung ist nur jQuery zu verwenden, um zu „reparieren“ Instanzen dieser Art. Führen Sie einfach die folgende Funktion nach der Seite (oder Teilseite) Lasten:

function makeHiddenInputBindable() { 
    $('input[type="hidden"]').each(
     function (i) { 
      $(this).attr('name', function() { 
       return this.name.replace(/\.\[/g, "["); 
      }) 
     } 
    ); 
} 
+1

jQuery sollte nicht als Krücke verwendet werden, um kaputtes HTML zu überarbeiten. Fix es auf der Serverseite. –