@формат HTML.HiddenFor не работает со списками в ASP.NET MVC
Я использую модель, которая содержит список как свойство. Я заполняю этот список элементами, которые я беру из SQL Server. Я хочу, чтобы список был скрыт в представлении и передан в действие POST. Позже я могу добавить больше элементов в этот список с помощью jQuery, что делает массив непригодным для расширения позже. Обычно вы бы использовали
@Html.HiddenFor(model => model.MyList)
для выполнения этой функции, но по какой-то причине список в POST всегда null.
очень простой вопрос, кто-нибудь знает, почему MVC ведет себя так?
12 ответов:
Я только что столкнулся с этой проблемой и решил ее, просто выполнив следующие действия:
for(int i = 0; i < Model.ToGroups.Count; i++) { @Html.HiddenFor(model => Model.ToGroups[i]) }
С помощью for вместо foreach привязка модели будет работать правильно и забрать все ваши скрытые значения в списке. Похоже, самый простой способ решить эту проблему.
HiddenFor не похож на DisplayFor или EditorFor. Он не будет работать с коллекциями, только отдельные значения.
вы можете использовать Serialize HTML helper, доступный в проекте MVC Futures, чтобы сериализовать объект в скрытое поле, или вам придется написать код самостоятельно. Лучшее решение - просто сериализовать какой-то идентификатор и повторно получить данные из базы данных при обратной передаче.
это немного хак, но если
@Html.EditorFor
или@Html.DisplayFor
работа для вашего списка, если вы хотите, чтобы убедиться, что он отправляется на запрос post, но не видно, вы можете просто стиль его с помощьюdisplay: none;
чтобы скрыть его вместо этого, например:<div style="display: none;">@Html.EditorFor(model => model.MyList)</div>
как насчет использования Newtonsoft для десериализации объекта в строку json, а затем вставить его в скрытое поле, например (модель.DataResponse.Сущность.Комиссия это список простых "CommissionRange" объекты, как вы увидите в JSON)
@using (Ajax.BeginForm("Settings", "AffiliateProgram", Model.DataResponse, new AjaxOptions { UpdateTargetId = "result" })) { string commissionJson = JsonConvert.SerializeObject(Model.DataResponse.Entity.Commission); @Html.HiddenFor(data => data.DataResponse.Entity.Guid) @Html.Hidden("DataResponse_Entity_Commission", commissionJson) [Rest of my form] }
обрабатывается как:
<input id="DataResponse_Entity_Commission" name="DataResponse_Entity_Commission" type="hidden" value="[{"RangeStart":0,"RangeEnd":0,"CommissionPercent":2.00000},{"RangeStart":1,"RangeEnd":2,"CommissionPercent":3.00000},{"RangeStart":2,"RangeEnd":0,"CommissionPercent":2.00000},{"RangeStart":3,"RangeEnd":2,"CommissionPercent":1.00000},{"RangeStart":15,"RangeEnd":10,"CommissionPercent":5.00000}]">
в моем случае я делаю некоторые вещи JS для редактирования json в скрытом поле перед отправкой обратно
в моем контроллере я затем использую Newtonsoft снова десериализовать:
string jsonCommissionRange = Request.Form["DataResponse_Entity_Commission"]; List<CommissionRange> commissionRange = JsonConvert.DeserializeObject<List<CommissionRange>>(jsonCommissionRange);
Html.HiddenFor
предназначен только для одного значения. Вам нужно будет сериализовать свой список каким-то образом, прежде чем создавать скрытое поле.например, если ваш список имеет тип string, вы можете объединить список в список, разделенный запятыми, а затем разделить список после отправки обратно в контроллер.
Я только что узнал (после нескольких часов попыток выяснить, почему значения модели не возвращались к контроллеру), что hidden for должен следовать за EditorFor.
Если я не делаю что-то еще неправильно это то, что я нашел. Я больше не совершу этой ошибки.
в контексте модели, которая содержит список другого класса.
это не сработает:
@{ for (int i = 0; i < Model.Categories.Count; i++) { <tr> <td> @Html.HiddenFor(modelItem => Model.Categories[i].Id) @Html.HiddenFor(modelItem => Model.Categories[i].ProductCategoryId) @Html.HiddenFor(modelItem => Model.Categories[i].CategoryName) @Html.DisplayFor(modelItem => Model.Categories[i].CategoryName) </td> <td> @Html.HiddenFor(modelItem => Model.Categories[i].DailyPurchaseLimit) @Html.EditorFor(modelItem => Model.Categories[i].DailyPurchaseLimit) @Html.ValidationMessageFor(modelItem => Model.Categories[i].DailyPurchaseLimit) </td> <td style="text-align: center"> @Html.HiddenFor(modelItem => Model.Categories[i].IsSelected) @Html.EditorFor(modelItem => Model.Categories[i].IsSelected) </td> </tr> } }
где, как это будет......
for (int i = 0; i < Model.Categories.Count; i++) { <tr> <td> @Html.HiddenFor(modelItem => Model.Categories[i].Id) @Html.HiddenFor(modelItem => Model.Categories[i].ProductCategoryId) @Html.HiddenFor(modelItem => Model.Categories[i].CategoryName) @Html.DisplayFor(modelItem => Model.Categories[i].CategoryName) </td> <td> @Html.EditorFor(modelItem => Model.Categories[i].DailyPurchaseLimit) @Html.HiddenFor(modelItem => Model.Categories[i].DailyPurchaseLimit) @Html.ValidationMessageFor(modelItem => Model.Categories[i].DailyPurchaseLimit) </td> <td style="text-align: center"> @Html.EditorFor(modelItem => Model.Categories[i].IsSelected) @Html.HiddenFor(modelItem => Model.Categories[i].IsSelected) </td> </tr> }
Я начал копаться в исходном коде
HiddenFor
, и я думаю, что блокпост вы видите, что ваш сложный объектMyList
неявно преобразуется в типstring
, Так что фреймворк лечит вашModel
значение какnull
и оказываетvalue
атрибут пустой.
вы можете взглянуть на это решение.
поместите только HiddenFor внутри EditorTemplate.
и на ваш взгляд поставить этот:
@Html.EditorFor(model => model.MyList)
Он должен работать.
столкнулась с той же проблемой. Без цикла for он опубликовал только первый элемент списка. После итерации через цикл for, он может сохранить полный список и опубликовать успешно.
@if (Model.MyList!= null) { for (int i = 0; i < Model.MyList.Count; i++) { @Html.HiddenFor(x => x.MyList[i]) } }
еще один возможный способ исправить это, чтобы дать каждому объекту в списке идентификатор, а затем использовать
@Html.DropDownListFor(model => model.IDs)
и заполнить массив, который содержит идентификаторы.
может быть, поздно, но я создал метод расширения для скрытых полей из коллекции (с простыми элементами типа данных):
Так вот:
/// <summary> /// Returns an HTML hidden input element for each item in the object's property (collection) that is represented by the specified expression. /// </summary> public static IHtmlString HiddenForCollection<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression) where TProperty : ICollection { var model = html.ViewData.Model; var property = model != null ? expression.Compile().Invoke(model) : default(TProperty); var result = new StringBuilder(); if (property != null && property.Count > 0) { for(int i = 0; i < property.Count; i++) { var modelExp = expression.Parameters.First(); var propertyExp = expression.Body; var itemExp = Expression.ArrayIndex(propertyExp, Expression.Constant(i)); var itemExpression = Expression.Lambda<Func<TModel, object>>(itemExp, modelExp); result.AppendLine(html.HiddenFor(itemExpression).ToString()); } } return new MvcHtmlString(result.ToString()); }
использование так же просто, как:
@Html.HiddenForCollection(m => m.MyList)