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

发布于 2024-09-27 23:29:42 字数 3547 浏览 6 评论 0原文

我正在尝试绑定属于较大视图模型一部分的列表,而无需借助自定义模型绑定程序。当我使用编辑器模板构建输入列表时,生成的名称的格式不正确,无法让默认活页夹正常工作。

而不是像我期望的那样 Items[3].Id 是 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 操作

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

I am attempting to bind a list that is part of a larger view model without resorting to a custom model binder. When I use an editor template to build the list of inputs, the generated names are not in the correct format for the default binder to work.

Instead of Items[3].Id like I would expect it is Items.[3].Id. If I build the list without an editor template it works as expected.

Am I doing something obviously wrong or is this just a quirk of Html.Hidden and 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>
<%
}%>

Output

<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 (Action Method)

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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

够钟 2024-10-04 23:29:42

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

首先,让我向您解释一下我通过检查 框架的源代码 学到的东西(检查总是一个好主意开源项目的源代码,以更好地理解某些事物的工作原理)。

1-) 当使用简单强类型 html 帮助器时(即除 EditorForHtml.xxxFor(...) 方法) strong> 和 DisplayFor),在定义要渲染的模型属性的 lambda 表达式中,生成的 html 元素的名称等于“model=>”后面的任何字符串,减去“=>”之前的内容,也就是说:

  • 字符串“model”(如果模型是集合
  • )或否则字符串“model.”(注意末尾的“.”)。

因此,例如这个:

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

==>这部分解释了为什么在使用 EditorFor 时会得到这个“奇怪”的 html 输出。

2-) 当使用复杂强类型帮助器(EditorFor 和 DisplayFor)时,相同的先前规则将应用于关联的分部视图(ItemList .ascx 在你的情况下),并且此外,所有生成的 html 元素都将以“==>”之后的内容前缀,如中所述1-)

这里的前缀是“Items.”,因为您的类型化视图 (Index.aspx) 中有这个:

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

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

解决方案是分解[HttpPost]中的ItemWrapper参数方法,进入他的属性,然后将绑定属性与每个复杂属性的前缀参数一起使用,如下所示:

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

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

为了避免冲突,在 post 方法中列出属性时,我强烈建议您根据每个属性的名称来命名您的参数(无论是案例)就像我一样。

希望这会有所帮助!

I have understood your problem, and i might very well have a solution too :)!

First, let me explain you what i have learned by inspecting the framework's source code (it's always a good idea to inspect an opensource project's source code to better understand how certain things work).

1-) When using simple strongly typed html helpers (i.e. all Html.xxxFor(...) methods except EditorFor and DisplayFor), in the lambda expression defining the model's property to render, the name of the html element generated is equals to whatever string follows "model=>", minus what comes before "=>", that is to say:

  • the string "model" if the model is a collection
  • or the string "model." (note the "." at the end) otherwise.

So, for example this :

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

will generate this html output:

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

And this:

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

will generate this:

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

==>This partly explains why you've got this "strange" html output when using EditorFor.

2-) When using complex strongly typed helpers (EditorFor and DisplayFor), the same previous rule is applied inside the associated partial view (ItemList.ascx in your case), and in addition, all generated html elements will be prefixed by what comes after "==>", as explained in 1-).

The prefix here is "Items.", because you have this in your typed view (Index.aspx):

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

==>This completely explains the output, and why default binder doesn't work anymore with your list of Items

The solution will be to break down your ItemWrapper parameter in the [HttpPost] method, into his properties, and then use the Bind Attribute with his Prefix parameter for each complex property, like this:

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

(supposing that ItemWrapper also has a simple property named Foo of type string)

To avoid conflict,when listing the properties in the post method, i strongly recommend you to name your parameters according to each property's name (no mather the case) like i did.

Hope this will help!

梦在深巷 2024-10-04 23:29:42

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

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

A more lazy solution is just to use jQuery in order to "fix" instances of this sort.
Just run the following function after the page (or partial page) loads:

function makeHiddenInputBindable() {
    $('input[type="hidden"]').each(
        function (i) {
            $(this).attr('name', function () {
                return this.name.replace(/\.\[/g, "[");
            })
        }
    );
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文