2010-10-17 3 views
8

Я пытаюсь связать список, который является частью более крупной модели представления, не прибегая к настраиваемому связующему объекту. Когда я использую шаблон редактора для создания списка входов, сгенерированные имена не соответствуют правильному формату работы связующего по умолчанию.ASP.NET MVC Model Binding IList в шаблоне редактора

Вместо предметов [3] .Id, как я ожидаю, это элементы. [3] .Id. Если я создам список без шаблона редактора, он работает так, как ожидалось.

Я делаю что-то явно неправильно или это просто причуда Html.Hidden и 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> 
<% 
}%> 

Выход

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

Редактировать (метод действий)

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 Действие

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

ответ

7

Я понял вашу проблему, и я мог бы очень хорошо иметь решение слишком :)!

Прежде всего, позвольте мне объяснить вам, что я узнал, проверив framework's source code (всегда полезно проверить исходный код проекта с открытым исходным кодом, чтобы лучше понять, как работают определенные вещи).

1-) При использовании простой сильно типизированных HTML хелперы (т.е. все Html.xxxFor (...) методы кроме EditorFor и Displayfor), в лямбда-выражения, определяющие свойство модели оказывать, название элемента HTML вырабатываемого равна любой строки следующим образом «модель =>», минус то, что предшествует «=>», то есть:

  • строка «модели», если модель является Collection
  • или строка "модели. "(обратите внимание на" . "в конце) в противном случае.

Так, например, это:

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

будет генерировать этот HTML вывод:

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

И это:

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

будет генерировать это:

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

==> Отчасти это объясняет, почему вы используете этот «странный» выход html при использовании редактора.

2-) При использовании сложных строго типизированных хелперы (EditorFor и Displayfor), то же самое предыдущее правило применяется внутри связанного частичный вид (ItemList.ascx в вашем случае), и в добавление, все сгенерированные элементы html будут префикс тем, что происходит после «==>», как объяснено в 1-).

Приставка здесь "товары", потому что у вас есть это в типизированной (index.aspx):

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

==> Это полностью объясняет выход, и почему по умолчанию связующее больше не работает с вашим списком предметов

решением будет пробоя вашего ItemWrapper Параметр в [HttpPost] Метод, в его свойствах, а затем использовать Bind Атрибут с его Prefix параметра для каждого комплексного свойства, например:

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

(если предположить, что ItemWrapper также имеет простое свойство с именем Foo типа строки)

чтобы избежать конфликта, при перечислении свойств в методе поста, я настоятельно рекомендую вам назвать свои параметры в соответствии с еа ch имя свойства (нет mather случай), как я.

Надеюсь, это поможет!

+0

Так что это действительно причуда в пути MVC генерирует имена полей. Частичное представление не учитывает, что модель является сборником при создании имени поля. Я думаю, если предметы.создается на уровне представления, а не на частичном уровне просмотра, тогда может не быть хорошего способа его исправить. Благодарю. –

+0

kondotine: звучит как ошибка asp.net mvc, кто-нибудь еще сообщил об этом? – Wout

+0

Хорошо, сообщил себе: http://aspnet.codeplex.com/workitem/7711, пожалуйста, проголосуйте за эту ошибку! – Wout

-1

Более ленивое решение - это просто использовать jQuery для «исправления» экземпляров такого рода. Просто запустите следующую функцию после страницы (или частичной страницы) нагрузки:

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

jQuery не следует использовать в качестве костыля для переделки разбитого HTML. Исправьте его на стороне сервера. –

Смежные вопросы