56

所以我有一个有问题的 MVC Asp.net 应用程序。本质上,我有一个包含表单的视图,其内容绑定到对象列表。在这个循环中,它会加载 PartialView 以及被循环的项目。现在一切正常,直到提交表单。当它被提交时,控制器会收到一个空的对象列表。下面的代码说明了这些问题。

父视图:

@model IEnumerable<PlanCompareViewModel>
@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" }))
{
<div>
    @foreach (var planVM in Model)
    {
        @Html.Partial("_partialView", planVM)
    }
</div>
}

_partialView:

@model PlanCompareViewModel
<div>
    @Html.HiddenFor(p => p.PlanID)
    @Html.HiddenFor(p => p.CurrentPlan)
    @Html.CheckBoxFor(p => p.ShouldCompare)
   <input type="submit" value="Compare"/>
</div>

这些是上述代码的类:

平面视图模型:

public class PlansCompareViewModel
{

    public int PlanID { get; set; }
    public Plan CurrentPlan { get; set; }
    public bool ShouldCompare { get; set; }
    public PlansCompareViewModel(Plan plan)
    {
        ShouldCompare = false;
        PlanID = plan.PlanId;
        CurrentPlan = plan;
    }

    public PlansCompareViewModel()
    {
        // TODO: Complete member initialization
    }
    public static IEnumerable<PlansCompareViewModel> CreatePlansVM(IEnumerable<Plan> plans)
    {
        return plans.Select(p => new PlansCompareViewModel(p)).AsEnumerable();
    }
}

控制器:

public class PlansController : MyBaseController
{
    [HttpPost]
    public ActionResult ComparePlans(IEnumerable<PlanCompareViewModel> model)
    {
         //the model passed into here is NULL
    }
}

问题出在控制器动作中。据我所知,它应该发布一个可枚举的 PlanCompareViewModels 列表,但它为空。在检查正在发送的发布数据时,它正在发送正确的参数。如果我将“IEnumerable”更改为“FormCollection”,它包含正确的值。谁能看到为什么活页夹没有创建正确的对象?我可以使用 javascript 解决这个问题,但这违背了目的!任何帮助将不胜感激!

4

2 回答 2

111

您的模型是null因为您向表单提供输入的方式意味着模型绑定器无法区分元素。现在,这段代码:

@foreach (var planVM in Model)
{
    @Html.Partial("_partialView", planVM)
}

没有为这些项目提供任何类型的索引。所以它会重复生成这样的 HTML 输出:

<input type="hidden" name="yourmodelprefix.PlanID" />
<input type="hidden" name="yourmodelprefix.CurrentPlan" />
<input type="checkbox" name="yourmodelprefix.ShouldCompare" />

但是,由于您要绑定到集合,因此您需要使用索引命名表单元素,例如:

<input type="hidden" name="yourmodelprefix[0].PlanID" />
<input type="hidden" name="yourmodelprefix[0].CurrentPlan" />
<input type="checkbox" name="yourmodelprefix[0].ShouldCompare" />
<input type="hidden" name="yourmodelprefix[1].PlanID" />
<input type="hidden" name="yourmodelprefix[1].CurrentPlan" />
<input type="checkbox" name="yourmodelprefix[1].ShouldCompare" />

该索引使模型绑定器能够关联单独的数据片段,从而使其能够构建正确的模型。所以这就是我建议你解决它的方法。与其使用局部视图循环遍历您的集合,不如利用模板的强大功能。以下是您需要遵循的步骤:

  1. EditorTemplates在视图的当前文件夹内创建一个文件夹(例如,如果您的视图是Home\Index.cshtml,则创建文件夹Home\EditorTemplates)。
  2. 在该目录中创建一个名称与您的模型匹配的强类型视图。在你的情况下,那将是PlanCompareViewModel.cshtml.

现在,您在局部视图中的所有内容都希望进入该模板:

@model PlanCompareViewModel
<div>
    @Html.HiddenFor(p => p.PlanID)
    @Html.HiddenFor(p => p.CurrentPlan)
    @Html.CheckBoxFor(p => p.ShouldCompare)
   <input type="submit" value="Compare"/>
</div>

最后,您的父视图被简化为:

@model IEnumerable<PlanCompareViewModel>
@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" }))
{
<div>
    @Html.EditorForModel()
</div>
}

DisplayTemplates并且EditorTemplates足够聪明,知道他们何时处理收藏品。这意味着它们会自动为您的表单元素生成正确的名称,包括索引,以便您可以正确地将模型绑定到集合。

于 2013-11-13T21:46:12.363 回答
37

请阅读以下内容:http: //haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
您应该为您的 html 元素“名称”属性设置索引,例如planCompareViewModel[0].PlanIdplanCompareViewModel[1].PlanId以使活页夹能够将它们解析为 IEnumerable。
而不是@foreach (var planVM in Model)使用for带有索引的循环和渲染名称。

于 2013-11-13T21:26:56.130 回答