MVC和数据绑定到模型的子选择

时间:2013-05-26 11:41:41

标签: asp.net-mvc data-binding razor

我想将视图绑定到我的自定义模型的集合。这不应该是任何问题,如果我采用这种方式来执行此操作,从我的视图循环遍历模型项,并在陈述模型时使用括号语法:

即:

@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不喜欢我的“选择”变量名??或者我的问题是什么,我怎么能解决它?

1 个答案:

答案 0 :(得分:1)

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

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

name="[0].ModelId"

稍后会是

name="[5].ModelId"

等等。

以下是您使用视图生成HTML的内容:

HTML source of view

这里有两个问题:

  1. 值“选择”出现在名称的前面
  2. 它使用零两次作为数组索引
  3. 这种情况正在发生,因为所有带有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(就名称属性而言):

    enter image description here

    希望这有帮助!