使用部分视图时,MVC模型在post上为空

我有一个MVC控制器,其中post方法上的模型总是返回null。 我不确定这是因为我在表单中使用了局部视图。

知道为什么模型没有返回控制器吗?

模型

在此处输入图像描述

加载模型

public List GetStaticMeasures(int businessUnitID) { List groups = ctx.Groups .Include("Datapoints") .Where(w => w.BusinessUnitID.Equals(businessUnitID)) .OrderBy(o => o.SortOrder).ToList(); groups.ForEach(g => g.Datapoints = g.Datapoints.OrderBy(d => d.SortOrder).ToList()); return groups; } 

调节器

 public ActionResult Data() { ViewBag.Notification = string.Empty; if (User.IsInRole(@"xxx\yyyyyy")) { List dataGroups = ctx.GetStaticMeasures(10); return View(dataGroups); } else { throw new HttpException(403, "You do not have access to the data."); } } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Data(List model) { ViewBag.Notification = string.Empty; if (User.IsInRole(@"xxx\yyyyyy")) { if (ModelState.IsValid) { ctx.SaveChanges(model); ViewBag.Notification = "Save Successful"; } } else { throw new HttpException(403, "You do not have access to save the data."); } return View(model); } 

主要观点

 @model List 
@using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true)
@foreach (var g in @Model) {

@g.Name

foreach (var d in g.Datapoints) { @Html.Partial("Measures", d) }
}
}

部分视图

 @model Jmp.StaticMeasures.Models.Datapoint @Html.HiddenFor(d => d.ID) @Html.HiddenFor(d => d.Name) @Html.HiddenFor(d => d.SortOrder) @Html.DisplayTextFor(d => d.Name) @Html.EditorFor(d => d.StaticValue) @Html.ValidationMessageFor(d => d.StaticValue) 

呈现显示连续ID的Html

在此处输入图像描述

正如你正确指出的那样,这是因为你使用的是偏爱。 发生这种情况是因为Html.Partial不知道它在一个集合上运行,所以它不会为你的表单元素生成你想要绑定到集合的名称。

但是,您的案例中的修复似乎相当简单。 您可以简单地将部分更改为EditorTemplate并在该模板上调用Html.EditorFor ,而不是使用Html.PartialHtml.EditorFor非常聪明,可以知道它何时处理集合,因此它将为集合中的每个项调用模板,在表单上生成正确的名称。

所以要做你需要的,请按照下列步骤操作:

  1. 在视图的当前文件夹中创建一个EditorTemplates文件夹(例如,如果您的视图是Home\Index.cshtml ,则创建文件夹Home\EditorTemplates )。 该名称很重要,因为它遵循查找模板的约定。
  2. 将部分视图放在该文件夹中。 或者,将其放在Shared\EditorTemplates文件夹中。
  3. 将部分视图重命名为Datapoint.cshtml (这很重要,因为模板名称基于类型名称的约定)。

现在相关的视图代码变为:

 // Note: I removed @ from Model here. @foreach (var g in Model) { 

@g.Name

@Html.EditorFor(m => g.DataPoints)
}

这可以确保您的视图分离,就像您最初的预期一样。

每条评论更新

好吧,正如我在下面提到的,现在的问题是模型绑定器无法将DataPoint与正确的Group相关联。 简单的解决方法是将视图代码更改为:

 for (int i = 0; i < Model.Count; i++) { 

@Model[i].Name

@Html.EditorFor(m => m[i].DataPoints)
}

这将正确生成名称,并应解决模型绑定问题。

OP的附录

按照John的回答,我还将Group表中缺少的属性包含在HiddenFor中,然后将该模型重新发布在post上。

 @for (int i = 0; i < Model.Count(); i++) { @Html.HiddenFor(t => Model[i].ID) @Html.HiddenFor(t => Model[i].BusinessUnitID) @Html.HiddenFor(t => Model[i].SortOrder) @Html.HiddenFor(t => Model[i].Name) 

@Model[i].Name

@Html.EditorFor(m => Model[i].Datapoints)
}

更新2 – 更清洁的解决方案

我对每个DataPoint使用EditorTemplate建议也适用于每个Group 。 而不是需要for循环,再次在视图中使用逻辑,您可以完全通过为Group设置EditorTemplate来避免这种情况。 在放置模板的位置方面,上述步骤同样适用。

在这种情况下,模板将是Group.cshtml ,如下所示:

 @model Jmp.StaticMeasures.Models.Group 

@Model.Name

@Html.EditorFor(m => m.DataPoints)

如上所述,这将为集合中的每个项调用模板,这也将为每个Group生成正确的索引。 您的原始视图现在可以简化为:

 @model List @using (Html.BeginForm()) { // Other markup @Html.EditorForModel(); } 

如果像这样返回,Binder无法绑定到对象列表。 是的,部分是你的问题。 您需要在表单中为ID指定一个数字。

做这样的事情:

  // pseudocode @model List 
@using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true)
for(int i; i@g.Name @Html.HiddenFor(d => Model[i].Id) @Html.HiddenFor(d => Model[i].Name) @Html.HiddenFor(d => Model[i].SortOrder) @Html.DisplayTextFor(d => Model[i].Name) @Html.EditorFor(d => Model[i].StaticValue) @Html.ValidationMessageFor(d => Model[i].StaticValue)
}
}

在Haack的博客中查看有关绑定到列表的更多详细信息

由于模型绑定器处理集合的方式,您将获得空模型。

您的局部视图正在渲染这些输入,例如:

  ... 

然后对List每个条目重复此操作。 不幸的是,模型绑定器不知道如何处理它,你将得到一个空值。

您的输入必须看的方式是:

  ...  

编号不能中断。 获得此function的一种方法是重写使用Html.xxxFor方法的方式,例如:迭代列表并执行以下操作:

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

以下是两个资源,详细解释了这一点,并提供了如何使模型绑定器与集合一起工作的其他示例:

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/