使用部分视图时,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.Partial
。 Html.EditorFor
非常聪明,可以知道它何时处理集合,因此它将为集合中的每个项调用模板,在表单上生成正确的名称。
所以要做你需要的,请按照下列步骤操作:
- 在视图的当前文件夹中创建一个
EditorTemplates
文件夹(例如,如果您的视图是Home\Index.cshtml
,则创建文件夹Home\EditorTemplates
)。 该名称很重要,因为它遵循查找模板的约定。 - 将部分视图放在该文件夹中。 或者,将其放在
Shared\EditorTemplates
文件夹中。 - 将部分视图重命名为
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://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/