@ Html.HiddenFor在ASP.NET MVC中的列表上不起作用


97

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

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

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

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


1
通常,您不会像这样隐藏整个列表。根据<input />s的期望输出是多少?
Cᴏʀʏ

1
是什么MyList内容?HiddenFor一次仅用于一个输入。
丹尼尔·怀特

1
什么类型的Model.MyList?您可能需要手动对列表执行一些序列化/反序列化。
凯尔·特劳伯曼


Answers:


161

我刚刚遇到了这个问题,只需执行以下操作即可解决该问题:

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

通过使用for而不是foreach,模型绑定将正确运行,并选择列表中的所有隐藏值。似乎是解决此问题的最简单方法。


5
谢谢!救了我一晚。
TSmith

7
谢谢-不错的简单解决方案。不过,只需要一个微小的mod:需要引用对象的ID字段。因此,如果将该字段称为RowId,则:@Html.HiddenFor(model => Model.ToGroups[i].RowId)
克里希纳·古普塔

3
为我工作,甚至在集合中的模型上有多个字段时也是如此。即@Html.EditorFor(model => Model.ToGroups[i].Id)随后@Html.EditorFor(model => Model.ToGroups[i].Description)的下一个时间-无论是在for循环。控制器能够将其映射到具有这些字段的模型列表。并且要确保所有内容均未出现在屏幕上,只需将其围在<div style="display: none;"></div>
Don Cheadle,2016年

辉煌!做得很好。为我工作!
AxleWack

3
@ user3186023在这里回答一个很老的评论,但也许其他人也会遇到相同的问题:将for-loop 更改为:for(int i = 0; i < Model.Departments.Count(); i++)
Stian

28

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

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


你有例子吗?我尝试了此操作,并且在提交表单时它未能绑定到ViewModel值。
艾伦·麦克唐纳

@AlanMacdonald-如果绑定失败,这是因为您的命名不正确,很可能是因为您使用了foreach而不是带有索引器的for。也许您没有在绑定中使用适当的属性。见weblogs.asp.net/shijuvarghese/archive/2010/03/06/...
埃里克Funkenbusch

谢谢。实际上,当我尝试使用它时,实际上是@ Html.Serialize(“ Model.ModelIDs”,Model.ModelIDs),其中Model是我的ViewModel,它具有ModelIDs int数组属性。因此,没有循环或任何东西。提交表单后,在绑定的ViewModel中,ModelID始终为null。
艾伦·麦克唐纳

@AlanMacdonald-名称中不包括“模型”。
Erik Funkenbusch

16

这有点骇人听闻,但是如果您要使用列表,@Html.EditorFor或者@Html.DisplayFor要在列表中使用它,请确保将其发送到发帖请求但不可见,您可以display: none;将其样式化为使用隐藏它,例如:

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

这不会在请求后将值保存在模型中。
nldev

如果.EditorFor设置为可以正常工作,那么我认为这也应该工作。
Mark Rhodes

9

如何使用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);

这对我有用。我认为接受的解决方案要干净得多。
e-

6

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

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


4

我刚刚发现(经过几个小时的尝试弄清楚为什么模型值没有回到控制器后),隐藏的对象应该跟随EditorFor。

除非我做错其他事情,否则这就是我发现的结果。我不会再犯错了。

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

这将不起作用:

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

3

我开始研究的源代码HiddenFor,并且我认为您遇到的障碍是您的复杂对象MyList不能隐式转换为type string,因此该框架将您的Model值视为null并将value属性呈现为空。


3

您可以看一下这个解决方案

仅将HiddenFor放在EditorTemplate中。

然后在您的视图中输入以下内容: @Html.EditorFor(model => model.MyList)

它应该工作。


3

面临同样的问题。没有for循环,它仅发布列表的第一个元素。在遍历for循环之后,它可以保留完整列表并成功发布。

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

2

另一种选择是:

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

这也是我的第一个想法。但是我有一个视图模型,它期望MyList字段的int []和逗号分隔的字符串不会通过MVC绑定机制解析为数组。
Tadej Mali

2

foreach循环而不是一个for循环可能是一个稍微清晰的解决方案。

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

1

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


1

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

所以这里是:

/// <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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.