0

I want to bind a view to a collection of my custom model. This should not be any problem, if I go for the generel way to do this, looping through the model items from my view, and using the bracket syntax when stating the model:

Ie:

@Html.HiddenFor(m => Model[i].Id)

My issue is that I need to do some grouping of my model items, and thus I've made a sub-selection of my items:

@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... *@
       ...
    }

Now, the problem is that my controller method, receiving the submitted form, gets an empty model. So the serialization of the model is broken at some point; maybe MVC doesn't like my "selection" variable name?.. Or what could my problem be, and how could I solve it?

4

1 回答 1

1

问题在于生成视图时为每个项目生成的名称。模型绑定器使用名称-值对来确定如何将表单值映射到控制器中期望的视图模型。

当您使用@Html.HiddenFor(m=>m[i].Id)时,这很好,并且会在您的 HTML 中生成以下名称属性:

name="[0].ModelId"

以后会是

name="[5].ModelId"

等等。

这是使用您的视图生成的 HTML:

HTML 视图源

这里有两个问题:

  1. 值“selection”出现在名称的前面
  2. 它使用零作为数组索引的两倍

之所以发生这种情况,是因为所有带有 for 的 HTML 标记都在最后工作(HiddenFor、TextboxFor 等)。他们根据 lambda 表达式确定名称属性 - 因为您m => m[0].ModelId在第一个表达式中使用,所以它用作[0].ModelId名称。在第二个中,您使用m=> selection[0].ModelId, 所以它使用selection[i].ModelId(它删除了您作为 lambda.xml 的一部分使用的任何变量。

那么,你如何解决这个问题?

在给定当前设置的情况下,您可以通过两种方式之一来解决此问题。您可以在循环之外使用计数器并将其用作数组索引,或者您可以利用额外的隐藏字段作为索引字段。对于任何一种方法,您都必须删除强类型的 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(就名称属性而言):

在此处输入图像描述

希望这会有所帮助!

于 2013-05-26T20:38:28.513 回答