模型绑定到MVC 3中可能存在非顺序索引的列表?

时间:2011-04-18 09:55:46

标签: asp.net-mvc list model-binding indices

我正在关注来自Phil Haack的http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx的信息

他谈到非顺序指数:

<form method="post" action="/Home/Create">

<input type="hidden" name="products.Index" value="cold" />
<input type="text" name="products[cold].Name" value="Beer" />
<input type="text" name="products[cold].Price" value="7.32" />

<input type="hidden" name="products.Index" value="123" />
<input type="text" name="products[123].Name" value="Chips" />
<input type="text" name="products[123].Price" value="2.23" />

<input type="hidden" name="products.Index" value="caliente" />
<input type="text" name="products[caliente].Name" value="Salsa" />
<input type="text" name="products[caliente].Price" value="1.23" />

<input type="submit" />
</form>

当你使用TextBoxFor进行模型绑定时,这是否可以在MVC3中使用? 这是使用sequentiel指数的方法:

@Html.TextBoxFor(m => m[i].Value)

如果不可能的话,如果我的指数不是连续的那么我还能做什么呢?

3 个答案:

答案 0 :(得分:1)

AFAI理解非顺序索引技术需要为每个索引值添加隐藏字段。我很确定Html.TextBoxFor帮助器本身不会生成任何额外的隐藏字段。您可以通过手动添加带有非顺序索引的隐藏字段来实现此目的。

答案 1 :(得分:0)

我试过这个,我无法使用textboxfor。我使用了Textbox并指定了名称。

<form method="post" action="/Home/Create">
@{var index = Guid.NewGuid();}
@Html.Textbox("products["+index +"].Name",Beer)
@Html.Textbox("products["+index +"].Price",7.32)
.
.
.
<input type="submit" />
</form>

您不需要MVC 3.0的隐藏索引。

让我知道如果这不起作用我有办法做到这一点,我只是从头脑中接受这个。

答案 2 :(得分:0)

为了不弄乱您的观点,我发现这样做的最简单方法是遵循以下扩展: BeginCollectionItem

完整的项目在这里:https://github.com/danludwig/BeginCollectionItem

但AFAIK你只需要这个课程:

public static class HtmlPrefixScopeExtensions
    {
        private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";

        public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
        {
            var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
            string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();

            // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
            html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));

            return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
        }

        public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
        {
            return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
        }

        private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
        {
            // We need to use the same sequence of IDs following a server-side validation failure,  
            // otherwise the framework won't render the validation error messages next to each item.
            string key = idsToReuseKey + collectionName;
            var queue = (Queue<string>)httpContext.Items[key];
            if (queue == null) {
                httpContext.Items[key] = queue = new Queue<string>();
                var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
                if (!string.IsNullOrEmpty(previouslyUsedIds))
                    foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
                        queue.Enqueue(previouslyUsedId);
            }
            return queue;
        }

        private class HtmlFieldPrefixScope : IDisposable
        {
            private readonly TemplateInfo templateInfo;
            private readonly string previousHtmlFieldPrefix;

            public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
            {
                this.templateInfo = templateInfo;

                previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
                templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
            }

            public void Dispose()
            {
                templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
            }
        }
    }

如何在您的观看中使用它

<form method="post" action="/Home/Create">
    @foreach (var item in Model.Products) {    
        @using (Html.BeginCollectionItem("Products"))
        { 
            @Html.TextBoxFor(item => item.Name)
            @Html.TextBoxFor(item => item.Price)            
        }
     } 
         ...
         ...
</form>

我认为这比查看视图中的索引要干净得多...... 以下是解释如何一步一步地执行此操作的帖子:http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/

Nuget Package: http://www.nuget.org/packages/BeginCollectionItem/