@Html.HiddFor在ASP.NET MVC中的列表上不起作用
本文关键字:列表 不起作用 MVC HiddFor ASP NET @Html | 更新日期: 2023-09-27 18:25:16
我使用的模型包含一个List作为属性。我正在用从SQL Server中获取的项目填充此列表。我希望列表隐藏在视图中,并传递给POST操作。稍后,我可能想用jQuery将更多的项目添加到这个列表中,这使得数组不适合稍后扩展
@Html.HiddenFor(model => model.MyList)
以实现此功能,但由于某些原因,POST中的List始终为null。
很简单的问题,有人知道MVC为什么会这样做吗?
我刚刚遇到这个问题,只需执行以下操作即可解决:
@for(int i = 0; i < Model.ToGroups.Count; i++)
{
@Html.HiddenFor(model => Model.ToGroups[i])
}
通过使用for
而不是foreach
,模型绑定将正常工作,并拾取列表中的所有隐藏值。这似乎是解决这个问题的最简单的方法。
您可以使用MVC Futures项目中提供的Serialize HTML帮助程序将对象序列化为隐藏字段,或者您必须自己编写代码。更好的解决方案是简单地序列化某种类型的ID,并在回发时从数据库中重新获取数据。
这有点破解,但如果@Html.EditorFor
或@Html.DisplayFor
适用于您的列表,如果您想确保它在发布请求时发送但不可见,您可以将其设置为使用display: none;
来隐藏它,例如:
<div style="display: none;">@Html.EditorFor(model => model.MyList)</div>
使用Newtonsoft将对象反序列化为json字符串,然后将其插入到Hidden字段中,例如。(Model.DataResponse.Entity.Commission是简单"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
仅为一个值设计。在创建隐藏字段之前,您需要以某种方式序列化列表。
例如,如果你的列表是字符串类型的,你可以将列表加入一个逗号分隔的列表,然后在控制器中发布后拆分列表。
我刚刚发现(经过几个小时的努力,试图弄清楚为什么模型值没有返回到控制器)隐藏的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
属性渲染为空。
您可以看看这个解决方案。
在EditorTemplate中只放入HiddeFor。
在你看来:@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)) />
foreach
循环而不是for
循环可能是一个稍微干净一点的解决方案。
@foreach(var item in Model.ToGroups)
{
@Html.HiddenFor(model => item)
}
解决此问题的另一种可能方法是为列表中的每个对象提供一个ID,然后使用@Html.DropDownListFor(model => model.IDs)
并填充一个包含ID的数组。
可能晚了,但我为集合中的隐藏字段创建了扩展方法(使用简单的数据类型项):
现在是:
/// <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)
除此之外,我还有一个具有各种属性的Model,其中一些属性是IEnumerables。我所做的是用在视图中的一个变量中序列化它
@{
var serializedObject = JsonSerializer.Serialize(Model);
}
然后,将该字符串放入一个Hidden元素中(因为HiddeFor用于模型中的单个值),如下所示:
@Html.Hidden("serialized", @serializedObject)
最后,在控制器中,我可以使用对其进行反序列化
JsonSerializer.Deserialize<MyType>(Request.Form["serialized"])