我想将视图绑定到我的自定义模型的集合。这不应该是任何问题,如果我采用这种方式来执行此操作,从我的视图循环遍历模型项,并在陈述模型时使用括号语法:
即:
@Html.HiddenFor(m => Model[i].Id)
我的问题是我需要对我的模型项进行一些分组,因此我对项目做了一个小选:
@foreach(var itemType in Model.GroupBy(item => item.Type).Select(grp => grp.First()))
{
<p>@itemType:</p>
var selection = Model
.Where(p => p.Type == itemType)
.OrderBy(p => p.CreationDate);
for (int i = 0; i < selection.Count(); i++) {
@Html.HiddenFor(m => selection[i].Id)
@* all my other element bindings here... *@
...
}
现在,问题是我的控制器方法,接收提交的表单,得到一个空模型。因此,模型的序列化在某些方面被打破;也许MVC不喜欢我的“选择”变量名??或者我的问题是什么,我怎么能解决它?
答案 0 :(得分:1)
问题在于生成视图时为每个项生成的名称。模型绑定器使用名称 - 值对来确定如何将表单值映射到控制器中您期望的视图模型
当您使用@Html.HiddenFor(m=>m[i].Id)
时,这很好并且会在HTML中生成以下名称属性:
name="[0].ModelId"
稍后会是
name="[5].ModelId"
等等。
以下是您使用视图生成HTML的内容:
这里有两个问题:
这种情况正在发生,因为所有带有for的HTML标签的工作方式(HiddenFor,TextboxFor等)。它们根据lambda表达式确定name属性 - 因为您在第一个中使用m => m[0].ModelId
,所以它使用[0].ModelId
作为名称。在第二个中,您使用m=> selection[0].ModelId
,因此它使用selection[i].ModelId
(它删除您作为lambda的一部分使用的任何变量。
那么,你如何解决这个问题?
在当前设置下,您可以通过两种方式之一来解决此问题。您可以使用循环外部的计数器并将其用作数组索引,或者您可以利用额外的隐藏字段作为索引字段。对于这两种方法,您将不得不删除强类型的HiddenFor / TextBoxFor并使用Hidden / TextBox,以便您可以手动设置正在生成的HTML元素的名称。
索引字段会发生什么,您的项目名称如下所示:
<input type="hidden" name="Index" value="Model1"/>
<input type="hidden" name="[Model1].ModelId" value="1"/>
<input type="hidden" name="[Model1].ModelValue" value="Some value"/>
在第一个输入标记上,我将其称为index并设置一些值作为此相关值集的索引。在剩下的,我使用值集作为第一个中的值作为数组索引,然后只使用属性的名称。如果您正在添加/删除列表中的项目,并且无法保证在发回时按顺序包含所有数字,则此方法最终会特别有用。
所以这就是你的Razor代码的样子:
for (int i = 0; i < selection.Count(); i++) {
@Html.Hidden("Index", "Model"+selection[i].ModelId)
@Html.Hidden("[Model"+selection[i].ModelId + "].ModelId", selection[i].ModelId)
@Html.Hidden("[Model"+selection[i].ModelId + "].CreateDate", selection[i].CreateDate)
@Html.Hidden("[Model"+selection[i].ModelId + "].ModelValue", selection[i].ModelValue)
作为旁注,我选择将“模型”这个词作为索引的一部分,因此它肯定知道它将其称为类似字典的条目,而不是数组中的数字位置。我不知道你是否绝对需要这样做,我还没有尝试过另一种方式。我使用ModelId作为索引值,因为它应该是一个唯一的值。
最终结果是你应该生成这样的HTML(就名称属性而言):
希望这有帮助!