@Html.HiddenFor 不适用于 ASP.NET MVC 中的列表

发布于 2025-01-07 14:44:15 字数 262 浏览 0 评论 0原文

我正在使用包含列表作为属性的模型。我用从 SQL Server 获取的项目填充此列表。我希望列表隐藏在视图中并传递给 POST 操作。稍后我可能想使用 jQuery 将更多项目添加到此列表中,这使得数组不适合稍后扩展。通常您会使用它

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

来完成此功能,但由于某种原因,POST 中的列表始终为空。

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

I'm using a model that contains a List as a property. I'm populating this list with items i grab from SQL Server. I want the List to be hidden in the view and passed to the POST action. Later on i may want to add more items to this List with jQuery which makes an array unsuitable for expansion later on. Normally you would use

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

to accomplish this functionality, but for some reason the List in POST is always null.

Very simple question, anyone know why MVC behaves like this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

泅人 2025-01-14 14:44:15

我刚刚遇到这个问题,并通过执行以下操作简单地解决了它:

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

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

I've just come across this issue and solved it simply by doing the following:

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

By using a for instead of a foreach the model binding will work correctly and pick up all of your hidden values in the list. Seems like the simplest way to solve this problem.

伴我心暖 2025-01-14 14:44:15

HiddenFor 与 DisplayFor 或 EditorFor 不同。它不适用于集合,仅适用于单个值。

您可以使用 MVC Futures 项目中提供的序列化 HTML 帮助程序将对象序列化到隐藏字段,否则您必须自己编写代码。更好的解决方案是简单地序列化某种 ID,并在回发时从数据库重新获取数据。

HiddenFor is not like a DisplayFor or EditorFor. It won't work with collections, only single values.

You can use the Serialize HTML helper available in the MVC Futures project to serialize an object to a Hidden field, or you will have to write the code yourself. A better solution is to simply serialize an ID of some sort and re-get the data from the database on postback.

游魂 2025-01-14 14:44:15

这有点像黑客,但如果 @Html.EditorFor@Html.DisplayFor 适用于您的列表,如果您想确保它在发布请求中发送,但不可见,您可以将其样式设置为使用 display: none; 来隐藏它,例如:

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

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>
栩栩如生 2025-01-14 14:44:15

怎么样使用 Newtonsoft 将对象反序列化为 json 字符串,然后将其插入到您的隐藏字段中,例如
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);

What about using Newtonsoft to deserialize the object into a json string and then insert that into your Hidden field e.g.
(Model.DataResponse.Entity.Commission is a List of simple "CommissionRange" objects as you'll see in the 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]
   }

Renders as:

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

In my case I do some JS stuff to edit the json in the hidden field before posting back

In my controller I then use Newtonsoft again to deserialize:

string jsonCommissionRange = Request.Form["DataResponse_Entity_Commission"];
List<CommissionRange> commissionRange = JsonConvert.DeserializeObject<List<CommissionRange>>(jsonCommissionRange);
半山落雨半山空 2025-01-14 14:44:15

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

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

Html.HiddenFor is designed for only one value. You will need to serialize your list in some way before creating the hidden field.

For example, if your list is of type string, you could join the list into a comma separated list, then split the list after post back in your controller.

魂归处 2025-01-14 14:44:15

我刚刚发现(经过几个小时试图弄清楚为什么模型值没有返回到控制器)隐藏的 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>
            }

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.

In the context of a Model that contains a list of another class.

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

Where as this WILL......

            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>
            }
风流物 2025-01-14 14:44:15

我开始深入研究 HiddenFor 的源代码,我认为您看到的障碍是您的复杂对象 MyList 不能隐式转换为 string< 类型/code>,因此框架将您的 Model 值视为 null 并将 value 属性呈现为空。

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.

彩虹直至黑白 2025-01-14 14:44:15

您可以查看这个解决方案

仅将 HiddenFor 放入 EditorTemplate 中。

在你的视图中输入: @Html.EditorFor(model => model.MyList)

它应该可以工作。

You can take a look on this solution.

Put only HiddenFor inside the EditorTemplate.

And in your View put this: @Html.EditorFor(model => model.MyList)

It should works.

等数载,海棠开 2025-01-14 14:44:15

面临同样的问题。如果没有 for 循环,它只发布列表的第一个元素。迭代for循环后,它可以保留完整列表并成功发布。

 @if (Model.MyList!= null)
    {
    for (int i = 0; i < Model.MyList.Count; i++)
      {
        @Html.HiddenFor(x => x.MyList[i])
      }
    }

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])
      }
    }
烟酉 2025-01-14 14:44:15

另一种选择是:

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

Another option would be:

<input type="hidden" value=@(string.Join(",", Model.MyList)) />
风吹雪碎 2025-01-14 14:44:15

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

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

The foreach loop instead of a for loop might be a slightly cleaner solution.

@foreach(var item in Model.ToGroups)
{
    @Html.HiddenFor(model => item)
}
巴黎盛开的樱花 2025-01-14 14:44:15

解决此问题的另一种可能方法是为列表中的每个对象提供一个 ID,然后使用 @Html.DropDownListFor(model => model.IDs) 并填充一个保存 ID 的数组。

Another possible way to fix this would be to give each object in your List an ID, then use @Html.DropDownListFor(model => model.IDs) and populate an array which holds the IDs.

卷耳 2025-01-14 14:44:15

也许晚了,但我为集合中的隐藏字段创建了扩展方法(使用简单的数据类型项):

所以这里是:

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

maybe late, but i created extension method for hidden fields from collection (with simple data type items):

So here it is:

/// <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());
}

Usage is as simple as:

@Html.HiddenForCollection(m => m.MyList)
最后的乘客 2025-01-14 14:44:15

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

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

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

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

最后,在控制器中,我可以反序列化它

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

Adding to this answer, I had a Model with various properties, some of which were IEnumerables. What I did was to serialize it in a variable in the view with:

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

And then, put that string into a Hidden element (since HiddenFor is intended for a single value from a Model) like this:

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

And finally, in the controller, I could deserialize it with

JsonSerializer.Deserialize<MyType>(Request.Form["serialized"])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文