我正在尝试使用EditorTemplate在父视图的表中显示子集合。我遇到的问题是,如果模板的名称与子类的名称完全相同,这似乎只能起作用。当我尝试使用名称略有不同的模板,并将该名称作为templateName参数传递给EditorFor时,我收到运行时错误。我希望我可以使用相同的子集合将不同的子EditorTemplates用于不同的目的。 这是一个简短的例子:
型号:
public class Customer
{
int id { get; set; }
public string name { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int id { get; set; }
public DateTime orderdate { get; set; }
public decimal amount { get; set; }
public Customer customer { get; set; }
}
客户控制器Index()方法:
public ActionResult Index()
{
Customer customer = new Customer() {id = 1, name = "Acme Corp.", Orders = new List<Order>()};
customer.Orders.Add(new Order() {id = 1, orderdate = DateTime.Now, amount = 100M});
customer.Orders.Add(new Order() { id = 2, orderdate = DateTime.Now, amount = 200M });
return View(customer);
}
客户Index.cshtml视图:
@model TemplateTest.Customer
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Customer</title>
</head>
<body>
<div>
@Html.EditorFor(Model=>Model.name)
<table>
<thead>
<tr>
<th>Order ID</th>
<th>Order Date</th>
<th>Amount</th>
</tr>
</thead>
@Html.EditorFor(Model=>Model.Orders)
</table>
</div>
</body>
</html>
Views / Shared / EditorTemplates中的Order.cshmtl模板(添加“颜色”以验证我使用此模板):
@model TemplateTest.Order
<tr>
<td>@Html.DisplayFor(Model=>Model.id)</td>
<td style="color:blue">@Html.EditorFor(Model=>Model.orderdate)</td>
<td>@Html.EditorFor(Model=>Model.amount)</td>
</tr>
这很好用。但是,如果我将EditorTemplate重命名为“OrderList.cshtml”并将子EditorFor行更改为
@Html.EditorFor(Model=>Model.Orders, "OrderList")
当我再次运行时,我得到了这个例外:
“传入字典的模型项类型为'System.Collections.Generic.List`1 [TemplateTest.Order]',但此字典需要类型为'TemplateTest.Order'的模型项。”
任何想法为什么EditorFor不使用我在“templateName”参数中指定的模板“OrderList”?否则,该参数是什么?
答案 0 :(得分:24)
TL; DR&gt;命名模板不适用于集合,使用foreach循环来解决它 - 请参阅下面有关原因的详细信息和示例。
你说:
任何想法为什么EditorFor不使用模板“OrderList”我 在“templateName”参数中指定?否则,那是什么 争论?
EditorFor
实际上是使用您指定的模板OrderList
- 但您偶然发现了一些非常令人困惑的内容。一些研究提出了很多提示,但我在这篇文章中找到了真正的细节:Problem with MVC EditorFor named template
简而言之,正在发生的是默认情况:@Html.EditorFor(Model=>Model.Orders)
实际上是按照惯例在临时调用MVC默认模板,但这根本不明显。
尝试这样思考:
在工作版本中,您传入List<Order>
类型,引用Model.Orders
(多个订单),但模板指定的模型为Order
(单个,不是很多) )。
有趣。为什么这甚至有效?乍一看似乎应该不工作。但它确实有效,因为幕后发生了什么。
从上述帖子转述:
使用
@Html.EditorFor(c => c.Orders)
MVC约定时选择IEnumerable
的默认模板。此模板是MVC框架的一部分,它的作用是生成Html.EditorFor()
枚举中的每个项目。然后该模板生成 适当的编辑器模板为列表中的每个项目 - 在您的情况下,它们都是Order
的所有实例,因此,Order
模板用于每个项目。
这是神奇的,它很方便,但因为这种情况按惯例发生,基本上对我们隐藏,所以我认为这是混乱的根源。
现在当您尝试执行相同的操作但使用命名模板时,明确设置EditorFor
以使用特定的编辑器模板OrderList
,您最终会得到编辑器模板传递整个枚举 - 这是您发布的错误的来源。
换句话说,失败的案例设法跳过工作案例的“魔术”部分,这就是它失败的原因。但是,在语义上它看起来很好听,对吧?有混乱。
your call default MVC template your template
@Html.EditorFor( Model => Model.Orders) IEnumerable template Order template
your call your template
@Html.EditorFor(Model=>Model.Orders, "OrderList") OrderList template ERROR!!!
有许多方法可以消除错误,但是其中许多方法都存在问题,因为它们会导致HTML控件的呈现方式使您无法通过POST上的索引来解决各个控件。 Uhhg。 (注意:工作案例确实按预期正确呈现HTML)
要正确呈现HTML控件,您似乎必须使用常规for
循环(而不是foreach
)并将每个Order
个对象传递给自定义模板(我称之为OrderEditorTemplateDefault
)。
@for (int i = 0; i < Model.Orders.Count ; i++)
{
@Html.EditorFor(c => Model.Orders[i], "OrderEditorTemplateDefault")
}
您的部分问题表明:
我希望我可以使用不同的子EditorTemplates进行不同的操作 具有相同子集的目的。
你可以通过在循环中引入条件并在那里选择备用模板(对于整个列表或逐个订单,只取决于你如何编写条件)来做到这一点。
@for (int i = 0; i < Model.Orders.Count ; i++) {
if (someCondition) {
@Html.EditorFor(c => Model.Orders[i], "OrderEditorTemplateDefault")
} else {
@Html.EditorFor(c => Model.Orders[i], "OrderEditorTemplateALTERNATE")
}
}
抱歉这么详细。希望有所帮助。