我有一个 MVC Controller ,其中 post 方法上的模型总是返回 null。我不确定这是否是因为我在表单中使用了部分 View 。
知道为什么模型没有返回给 Controller 吗?
型号
加载模型
public List<Group> GetStaticMeasures(int businessUnitID)
{
List<Group> 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;
}
Controller
public ActionResult Data()
{
ViewBag.Notification = string.Empty;
if (User.IsInRole(@"xxx\yyyyyy"))
{
List<Group> 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<Group> 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<Jmp.StaticMeasures.Models.Group>
<div class="row">
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<div class="large-12">
<div class="large-8 large-centered columns panel">
@foreach (var g in @Model)
{
<h2>@g.Name</h2>
foreach (var d in g.Datapoints)
{
@Html.Partial("Measures", d)
}
<hr />
}
<input type="submit" class="button" value="Save Changes"/>
</div>
</div>
}
</div>
局部 View
@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)
呈现的 Html 显示连续的 ID
最佳答案
正如您正确指出的那样,这是因为您使用的是部分。发生这种情况是因为 Html.Partial
不知道它在集合上运行,因此它不会为您的表单元素生成名称以绑定(bind)到集合。
但是,您的案例中的修复似乎相当简单。无需使用 Html.Partial
,您只需将部分内容更改为 EditorTemplate
并在该模板上调用 Html.EditorFor
。 Html.EditorFor
足够聪明,知道它何时处理一个集合,因此它会为集合中的每个项目调用您的模板,在您的表单上生成正确的名称。
因此,要执行您需要的操作,请按照以下步骤操作:
- 在 View 的当前文件夹中创建一个
EditorTemplates
文件夹(例如,如果您的 View 是Home\Index.cshtml
,则创建文件夹Home\EditorTemplates
).该名称很重要,因为它遵循查找模板的约定。 - 将您的部分 View 放在该文件夹中。或者,将其放在
Shared\EditorTemplates
文件夹中。 - 将您的部分 View 重命名为
Datapoint.cshtml
(这很重要,因为模板名称基于类型名称的约定)。
现在相关的 View 代码变为:
// Note: I removed @ from Model here.
@foreach (var g in Model)
{
<h2>@g.Name</h2>
@Html.EditorFor(m => g.DataPoints)
<hr />
}
这确保了 View 的分离,正如您最初预期的那样。
根据评论更新
好吧,正如我在下面提到的,现在的问题是模型绑定(bind)器无法将 DataPoint
与正确的 Group
相关联。简单的解决方法是将 View 代码更改为:
for (int i = 0; i < Model.Count; i++)
{
<h2>@Model[i].Name</h2>
@Html.EditorFor(m => m[i].DataPoints)
<hr />
}
这将正确生成名称,并且应该可以解决模型绑定(bind)问题。
OP 的附录
在 John 的回答之后,我还在 Group 表中包含了缺失的属性作为 HiddenFor's which game me the model back on the 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)
<h2>@Model[i].Name</h2>
@Html.EditorFor(m => Model[i].Datapoints)
<hr />
}
更新 2 - 更清洁的解决方案
我对每个 DataPoint
使用 EditorTemplate
的建议也适用于每个 Group
。不需要 for
循环,再次在 View 中添加逻辑,您可以通过为 Group
设置一个 EditorTemplate
来完全避免这种情况。就放置模板的位置而言,适用与上述相同的步骤。
在这种情况下,模板将是 Group.cshtml
,如下所示:
@model Jmp.StaticMeasures.Models.Group
<h2>@Model.Name</h2>
@Html.EditorFor(m => m.DataPoints)
<hr />
如上所述,这将为集合中的每个项目调用模板,这还将为每个 Group
生成正确的索引。您的原始 View 现在可以简化为:
@model List<Jmp.StaticMeasures.Models.Group>
@using (Html.BeginForm())
{
// Other markup
@Html.EditorForModel();
}
关于c# - 使用局部 View 时 MVC 模型 Null on post,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21574961/