快速刷新。
什么时候:
- 该模型具有类型的属性
IEnumerable<T>
- 您将此属性传递给
Html.EditorFor()
使用仅接受 lambda 表达式的重载 T
您在 Views/Shared/EditorTemplates 下有一个类型的编辑器模板
然后 MVC 引擎将自动为可枚举序列中的每个项目调用编辑器模板,生成结果列表。
例如,当有一个Order
具有属性的模型类时Lines
:
public class Order
{
public IEnumerable<OrderLine> Lines { get; set; }
}
public class OrderLine
{
public string Prop1 { get; set; }
public int Prop2 { get; set; }
}
并且有一个视图Views/Shared/EditorTemplates/OrderLine.cshtml:
@model TestEditorFor.Models.OrderLine
@Html.EditorFor(m => m.Prop1)
@Html.EditorFor(m => m.Prop2)
然后,当您@Html.EditorFor(m => m.Lines)
从顶级视图调用时,您将获得一个页面,其中包含每个订单行的文本框,而不仅仅是一个。
但是,正如您在链接问题中看到的那样,这仅在您使用EditorFor
. 如果您提供模板名称(为了使用不是以OrderLine
类命名的模板),则不会发生自动序列处理,而是会发生运行时错误。
此时,您必须将自定义模板的模型声明为IEnumebrable<OrderLine>
并以某种方式手动迭代其项目以输出所有项目,例如
@foreach (var line in Model.Lines) {
@Html.EditorFor(m => line)
}
这就是问题开始的地方。
以这种方式生成的 HTML 控件都具有相同的 id 和名称。当您稍后发布它们时,模型绑定器将无法构造OrderLine
s 数组,您在控制器中的 HttpPost 方法中获得的模型对象将是null
.
如果您查看 lambda 表达式,这是有道理的——它并没有真正将正在构造的对象链接到它来自的模型中的某个位置。
我尝试了各种迭代项目的方法,似乎唯一的方法是将模板的模型重新声明为IList<T>
并用以下方式枚举它for
:
@model IList<OrderLine>
@for (int i = 0; i < Model.Count(); i++)
{
@Html.EditorFor(m => m[i].Prop1)
@Html.EditorFor(m => m[i].Prop2)
}
然后在顶层视图中:
@model TestEditorFor.Models.Order
@using (Html.BeginForm()) {
@Html.EditorFor(m => m.Lines, "CustomTemplateName")
}
它提供了正确命名的 HTML 控件,这些控件在提交时被模型绑定器正确识别。
虽然这有效,但感觉非常错误。
什么是使用自定义编辑器模板的正确惯用方式EditorFor
,同时保留允许引擎生成适合模型绑定器的 HTML 的所有逻辑链接?