2010-10-17 16 views
8

Próbuję powiązać listę, która jest częścią większego modelu widoku bez odwoływania się do niestandardowego segregatora modelu. Kiedy używam szablonu edytora do budowania listy wejść, wygenerowane nazwy nie mają poprawnego formatu, aby domyślny spinacz działał.ASP.NET MVC Wiązanie modelu IList w szablonie edytora

Zamiast pozycji [3] .Podobnie jakbym oczekiwał, że to Elementy. [3] .Id. Jeśli zbuduję listę bez szablonu edytora, działa ona zgodnie z oczekiwaniami.

Czy robię coś oczywistego nie tak, czy jest to tylko dziwactwo Html.Hidden i 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> 
<% 
}%> 

Wyjście

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

Edit (sposób działania)

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 Action

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

Odpowiedz

7

zrozumiałem swój problem, a ja może przynieść rozwiązanie zbyt :)!

Po pierwsze, pozwól mi wyjaśnić, czego się nauczyłem, sprawdzając numer framework's source code (zawsze dobrze jest sprawdzić kod źródłowy projektu open source, aby lepiej zrozumieć, jak pewne rzeczy działają).

1-) Przy korzystaniu prosty silnie wpisane html pomocników (czyli wszystko Html.xxxFor (...) metody wyjątkiem EditorFor i Displayfor), w wyrażeniu lambda określające właściwość modelki renderowanie, nazwa elementu hTML generowany jest równa co następuje ciąg "Model =>", minus to, co jest przed "=>", to znaczy:

  • ciąg "modelu", jeśli model jest collection
  • lub napisu "modelu. "(należy zwrócić uwagę na" . "na końcu) w przeciwnym razie.

Tak więc, na przykład tak:

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

wygeneruje to wyjście HTML:

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

a to:

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

wygeneruje to:

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

==> To częściowo wyjaśnia, dlaczego masz "dziwne" wyjście html podczas korzystania z EditorFor.

2-) Przy korzystaniu złożonych silnie wpisane pomocników (EditorFor i Displayfor) obowiązuje poprzednia reguła jest stosowane wewnątrz związanego częściowym widokiem (ItemList.ascx w danym przypadku), a w dodanie, wszystkie wygenerowane elementy html będą z prefiksem według tego, co jest po "==>", jak wyjaśniono w 1-).

Przedrostek jest tutaj "przedmioty", bo masz to w swoim wpisany widzenia (Index.aspx):

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

==> To całkowicie wyjaśnia wyjścia i dlaczego domyślnego spoiwo nie działa już z listą elementów

rozwiązaniem będzie załamania Twój ItemWrapper Parametr w [HttpPost] Metoda, na jego właściwości, a następnie za pomocą Bind Atrybut jego prefiksu parametru na każdy zestaw właściwości, na przykład:

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

(zakładając, że ItemWrapper również prosty właściwość o nazwie Foo od typu string)

aby uniknąć konfliktu, podczas dodawania właściwości w metodzie post, i zalecamy, aby wymienić swoje parametry według EA ch nazwa nieruchomości (tak nie jest), tak jak ja.

Mam nadzieję, że to pomoże!

+0

Więc to naprawdę dziwactwo w sposób MVC generuje nazwy pól. Widok częściowy nie bierze pod uwagę, że model jest zbiorem podczas generowania nazwy pola. Myślę, że jeśli przedmioty.jest tworzony na poziomie widoku zamiast częściowego poziomu widoku, więc może nie być dobrym sposobem, aby to naprawić. Dzięki. –

+0

kondotine: brzmi jak błąd mvc asp.net, czy ktoś jeszcze to zgłosił? – Wout

+0

Ok, sam zgłosiłem: http://aspnet.codeplex.com/workitem/7711, proszę zagłosuj na tę poprawkę! – Wout

-1

Bardziej leniwym rozwiązaniem jest użycie jQuery w celu "naprawienia" wystąpień tego rodzaju. Wystarczy uruchomić następującą funkcję po stronie (lub częściowego stronie) obciążenia:

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

jQuery nie powinien być używany jako podpórka do przerobienia uszkodzonego kodu HTML. Napraw to po stronie serwera. –

Powiązane problemy