@Html.HiddenFor does not work on Lists in ASP.NET MVC

我使用了一个包含 List 作为属性的模型。我用从 SQLServer 获取的项填充此列表。我希望 List 隐藏在视图中并传递给 POST 操作。稍后,我可能会用 jQuery 向这个 List 添加更多的项,这会使数组不适合以后的展开。通常你会用

@Html.HiddenFor(model => model.MyList)

但由于某些原因,POST 中的 List 总是 null。

很简单的问题,有人知道为什么 MVC 会这样吗?

97364 次浏览

Html.HiddenFor仅为一个值设计。在创建隐藏字段之前,您需要以某种方式序列化列表。

例如,如果您的列表是字符串类型的,那么您可以将列表连接到逗号分隔的列表中,然后在控制器中分割发送后的列表。

HiddenFor 不像 DisplayFor 或 EditorFor,它不能处理集合,只能处理单个值。

您可以使用 MVC Futures 项目中提供的 Serialize HTML 助手将对象序列化到一个 Hidden 字段,或者您将不得不自己编写代码。更好的解决方案是简单地序列化某种 ID 并在回发时从数据库中重新获取数据。

I started digging through the source code for HiddenFor, and I think the roadblock you're seeing is that your complex object MyList is not implicitly convertible to type string, so the framework treats your Model value as null and renders the value attribute empty.

你可以看看这个 解决方案

在 EditorTemplate 中只放置 HiddenFor。

在你的视图中放这个: @Html.EditorFor(model => model.MyList)

应该可以。

It's a bit of a hack, but if @Html.EditorFor or @Html.DisplayFor work for your list, if you want to make sure it's sent on the post request but not visible, you could just style it to using display: none; to hide it instead, e.g:

<div style="display: none;">@Html.EditorFor(model => model.MyList)</div>

另一种可能的解决方法是给 List 中的每个对象一个 ID,然后使用 @Html.DropDownListFor(model => model.IDs)并填充一个包含 ID 的数组。

我只是偶然发现这个问题,并通过以下方法解决了它:

@for(int i = 0; i < Model.ToGroups.Count; i++)
{
@Html.HiddenFor(model => Model.ToGroups[i])
}

通过使用 for而不是 foreach,模型绑定将正常工作,并拾取列表中所有的隐藏值。似乎是解决这个问题最简单的方法。

I've just found out (after a couple of hours of trying to figure out why model values weren't going back to the controller) that hidden for should follow the EditorFor.

Unless I am doing something else wrong this is what I found. I will not make the mistake again.

在包含另一个类的列表的 Model 的上下文中。

This will NOT work:

        @{
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>
}

使用 Newtonsoft 将对象反序列化为 json 字符串,然后将其插入到 hide 字段中怎么样。 (模型。数据响应。实体。委托是简单 “调整范围”对象的 名单,如您将在 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

在我的控制器中,我再次使用 Newton soft 来反序列化:

string jsonCommissionRange = Request.Form["DataResponse_Entity_Commission"];
List<CommissionRange> commissionRange = JsonConvert.DeserializeObject<List<CommissionRange>>(jsonCommissionRange);

可能有点晚,但是我为集合中的隐藏字段创建了扩展方法(使用简单的数据类型条目) :

所以,事情是这样的:

/// <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)

Faced the same issue. Without for loop, it only posted the first element of the list. After iterating through for loop, it can keep full list and post successfully.

 @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)) />

使用 foreach循环代替 for循环可能是一种更简洁的解决方案。

@foreach(var item in Model.ToGroups)
{
@Html.HiddenFor(model => item)
}

添加到 这个答案中,我得到了一个具有各种属性的 Model,其中一些是 IEnumerables。我所做的是在视图中将其序列化为一个变量:

@{
var serializedObject = JsonSerializer.Serialize(Model);
}

然后,将该字符串放入一个 Hidden 元素(因为 HiddenFor 用于 Model 中的单个值) ,如下所示:

@Html.Hidden("serialized", @serializedObject)

最后,在控制器中,我可以用

JsonSerializer.Deserialize<MyType>(Request.Form["serialized"])