@формат 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. Он не будет работать с коллекциями, только с одиночными значениями.

вы можете использовать помощник сериализации HTML, доступный в проекте MVC Futures для сериализации объекта в скрытое поле, или вам придется написать код самостоятельно. Лучшее решение - просто сериализовать идентификатор и повторно получить данные из базы данных при обратной передаче.


это немного взломать, но если @Html.EditorFor или @Html.DisplayFor работа для вашего списка, если вы хотите убедиться, что он отправлен по запросу post, но не виден, вы можете просто стиль его с помощью display: none; чтобы скрыть его вместо этого, e.g:

<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])
      }
    }

другой вариант:

<input type="hidden" value=@(string.Join(",", Model.MyList)) />

другим возможным способом исправить это было бы дать каждому объекту в вашем списке идентификатор, а затем использовать @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)