ASP.NET MVC模型在编辑器模板中绑定IList

时间:2010-10-17 00:28:30

标签: asp.net-mvc modelbinders

我试图绑定一个列表,该列表是较大视图模型的一部分,而不需要使用自定义模型绑定器。当我使用编辑器模板来构建输入列表时,生成的名称格式不正确,默认绑定器可以工作。

而不是物品[3]。就像我期望它是物品。[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行动

[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 个答案:

答案 0 :(得分:7)

我理解你的问题,我也可能有一个解决方案:)!

首先,让我通过检查framework's source code向您解释我学到了什么(检查开源项目的源代码以更好地理解某些事情的工作方式总是一个好主意。)

1 - )使用简单的强类型 html帮助程序(即除之外的所有 Html.xxxFor(...)方法EditorFor DisplayFor ),在定义要呈现的模型属性的lambda表达式中,生成的html元素的名称等于“ model =&gt;”后面的任何字符串“,减去之前的” =&gt; “,也就是说:

  • 字符串“型号”如果型号是集合
  • 或字符串“型号。”(注意最后的“”)否则。

所以,例如:

<%: 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" ../>

==&gt;这部分解释了为什么在使用EditorFor时你有这个“奇怪的”html输出。

2 - )使用复杂的强类型辅助工具(EditorFor和DisplayFor)时,相关的先前规则将应用于关联的部分视图( ItemList.ascx 在你的情况下),另外,所有生成的html元素将在<==&gt;之后的前缀,如 1 - 。)

这里的前缀是“项目。”,因为您在键入的视图中有这个(Index.aspx):

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

==&gt;这完全解释了输出,以及为什么默认活页夹不再适用于您的项目列表

解决方案是 [HttpPost]方法中的 ItemWrapper 参数分解为属性,然后使用< strong>绑定属性,每个复杂属性的前缀参数,如下所示:

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

(假设ItemWrapper还有一个名为 Foo 的字符串类型的简单属性)

为了避免冲突,当在post方法中列出属性时,我强烈建议您根据每个属性的名称命名参数(没有其他情况),就像我一样。

希望这会有所帮助!

答案 1 :(得分:-1)

更懒惰的解决方案就是使用jQuery来“修复”此类实例。 只需在页面(或部分页面)加载后运行以下功能:

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