Возможно ли связывание модели со списком с непоследовательными индексами в MVC 3? - PullRequest
5 голосов
/ 18 апреля 2011

Я слежу за информацией по 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>

Возможно ли это в MVC3 при использованиипривязка модели с TextBoxFor?
Это способ сделать это с помощью последовательных индексов:

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

Если это невозможно, могу ли я что-нибудь еще сделать, если мои индексы не будут последовательными?

Ответы [ 3 ]

1 голос
/ 21 июня 2011

AFAI понимает, что техника непоследовательных индексов потребует добавления скрытых полей для каждого значения индекса. Я совершенно уверен, что сам помощник Html.TextBoxFor не создает никаких дополнительных скрытых полей. Вероятно, вы могли бы достичь этого, добавив скрытые поля с непоследовательными индексами вручную.

0 голосов
/ 04 декабря 2013

Чтобы не мешать вашему взгляду, я нашел самый простой способ сделать это: 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: http://www.nuget.org/packages/BeginCollectionItem/

0 голосов
/ 11 июля 2011

Я попробовал это и не смог заставить его работать с 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.

Дайте мне знать, если это не сработает, у меня есть способ сделать это, я просто беру это с макушки головы.

...