Привязка модели ASP.NET MVC к IList в шаблоне редактора - PullRequest
8 голосов
/ 17 октября 2010

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

Вместо Items [3]. Мне бы хотелось, чтобы это были Items. [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 } }
      }
    });
}

Редактировать # 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>

Ответы [ 2 ]

7 голосов
/ 01 ноября 2010

Я понял вашу проблему, и у меня вполне может быть решение тоже:)!

Во-первых, позвольте мне объяснить вам, что я узнал, изучив исходный код фреймворка (всегда полезно проверить исходный код проекта с открытым исходным кодом, чтобы лучше понять, как работают определенные вещи.

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

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

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

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

сгенерирует этот html output:

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

И это:

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

сгенерирует это:

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

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

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

Префикс здесь: " Items. ", потому что у вас есть это втипизированное представление (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 типа string)

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

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

0 голосов
/ 05 ноября 2010

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

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