MVC3非顺序索引和DefaultModelBinder



是真的,默认模型绑定在MVC 3.0是能够处理非顺序索引(简单和复杂的模型类型)?我遇到过建议应该这样做的帖子,但在我的测试中,它似乎没有。

给定的post back值:

items[0].Id = 10
items[0].Name = "Some Item"
items[1].Id = 3
items[1].Name = "Some Item"
items[4].Id = 6
items[4].Name = "Some Item"

和控制器方法:

public ActionResult(IList<MyItem> items) { ... }

只加载项0和1;第4项完全被忽略了。

我见过许多生成自定义索引(模型绑定到列表)的解决方案,但是它们似乎都针对以前版本的MVC,而且大多数都有点"笨拙"。

我错过了什么吗?

我有这个工作,你必须记住添加一个常见的索引隐藏输入,在你的参考文章中解释:

name = Items.Index的隐藏输入是关键部分

<input type="hidden" name="Items.Index" value="0" />
<input type="text" name="Items[0].Name" value="someValue1" />
<input type="hidden" name="Items.Index" value="1" />
<input type="text" name="Items[1].Name" value="someValue2" />
<input type="hidden" name="Items.Index" value="3" />
<input type="text" name="Items[3].Name" value="someValue3" />
<input type="hidden" name="Items.Index" value="4" />
<input type="text" name="Items[4].Name" value="someValue4" />

希望能有所帮助

您引用的文章是一篇老文章(MVC2),但据我所知,这仍然是使用默认模型绑定器为绑定集合建模的实际方法。

如果你想要非顺序索引,就像Bassam说的,你需要指定一个索引器。索引器不需要是数字。

我们使用了Steve Sanderson的BeginCollectionItem Html Helper。它自动生成索引器作为Guid。我认为当您的集合项HTML是非顺序的时,这是比使用数字索引器更好的方法。

这个helper方法,源自Steve Sanderson的方法,要简单得多,可以用来锚定集合中的任何项,它似乎可以与MVC模型绑定一起工作。

public static IHtmlString AnchorIndex(this HtmlHelper html)
{
    var htmlFieldPrefix = html.ViewData.TemplateInfo.HtmlFieldPrefix;
    var m = Regex.Match(htmlFieldPrefix, @"([w]+)[([w]*)]");
    if (m.Success && m.Groups.Count == 3)
        return
            MvcHtmlString.Create(
                string.Format(
                    "<input type="hidden" name="{0}.index" autocomplete="off" value="{1}" />",
                    m.Groups[1].Value, m.Groups[2].Value));
    return null;
}

。只需在EditorTemplate中调用它,或者在任何您将生成输入的地方调用它,如下所示,以生成索引锚定隐藏变量(如果有的话)。

@model SomeViewModel
@Html.AnchorIndex()
@Html.TextBoxFor(m => m.Name)
... etc.

我认为它比Steve Sanderson的方法有几个优点。

  1. 它与EditorFor和其他处理枚举的内置机制一起工作。因此,如果Items是视图模型上的IEnumerable<T>属性,则如下所示:

    <ul id="editorRows" class="list-unstyled"> @Html.EditorFor(m => m.Items) @* Each item will correctly anchor allowing for dynamic add/deletion via Javascript *@ </ul>

  2. 你可以有一个单一的EditorTemplate/DisplayTemplate的数据类型,它将简单地no-op,如果不用于列表中的项目。

唯一的缺点是,如果被绑定的根模型是可枚举的(即Action方法本身的参数,而不是参数对象图中更深层次的属性),那么绑定将在第一个非顺序索引处失败。不幸的是,DefaultModelBinder的.Index功能只适用于非根对象。在这种情况下,您唯一的选择仍然是使用上述方法。

这个星期我一直在纠结这个问题,巴萨姆的回答是让我走上正轨的关键。我有一个库存项目的动态列表,可以有一个数量字段。我需要知道他们选择了多少个项目,除了项目列表可以从1到n

最后我的解决方案相当简单。我创建了一个名为ItemVM的ViewModel,它有两个属性。ItemID和Quantity。在帖子中,我接受了这些列表。有了索引,所有的项目都被传递。即使是零量。您必须在服务器端验证和处理它,但是通过迭代处理这个动态列表是微不足道的。

在我的视图中,我使用这样的东西:

@foreach (Item item in Items)
{
<input type="hidden" name="OrderItems.Index" value="@item.ItemID" />
<input type="hidden" name="OrderItems[@item.ItemID].ItemID" value="@item.ItemID" />
<input type="number" name="OrderItems[@item.ItemID].Quantity" />
}

这给了我一个List和一个基于0的索引,但是控制器中的迭代从一个新的强类型模型中提取所有必要的数据。

public ActionResult Marketing(List<ItemVM> OrderItems)
...
        foreach (ItemVM itemVM in OrderItems)
            {
                OrderItem item = new OrderItem();
                item.ItemID = Convert.ToInt16(itemVM.ItemID);
                item.Quantity = Convert.ToInt16(itemVM.Quantity);
                if (item.Quantity > 0)
                {
                    order.Items.Add(item);
                }
            }

最后将得到一个数量大于0的Item集合,以及Item ID。

这项技术在MVC 5中使用Visual Studio 2015中的EF 6。也许这会帮助像我一样寻找解决方案的人。

或者使用这个javascript函数来修复索引:(替换EntityName和FieldName)

function fixIndexing() {
        var tableRows = $('#tblMyEntities tbody tr');
        for (x = 0; x < tableRows.length; x++) {
            tableRows.eq(x).attr('data-index', x);
            tableRows.eq(x).children('td:nth-child(1)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName1");
            tableRows.eq(x).children('td:nth-child(2)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName2");
            tableRows.eq(x).children('td:nth-child(3)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName3");
        }
        return true; //- Submit Form -
    }

我最终制作了一个更通用的HTML Helper:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
namespace Wallboards.Web.Helpers
{
    /// <summary>
    /// Hidden Index Html Helper
    /// </summary>
    public static class HiddenIndexHtmlHelper
    {
        /// <summary>
        /// Hiddens the index for.
        /// </summary>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <typeparam name="TProperty">The type of the property.</typeparam>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <param name="expression">The expression.</param>
        /// <param name="index">The Index</param>
        /// <returns>Returns Hidden Index For</returns>
        public static MvcHtmlString HiddenIndexFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int index)
        {
            var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            var propName = metadata.PropertyName;
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("<input type="hidden" name="{0}.Index" autocomplete="off" value="{1}" />", propName, index);
            return MvcHtmlString.Create(sb.ToString());
        }
    }
}

然后将它包含在Razor视图中列表元素的每次迭代中:-

@Html.HiddenIndexFor(m => m.ExistingWallboardMessages, i)

相关内容

  • 没有找到相关文章

最新更新