这是一个简短的例子:
楷模:
- 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,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>
在View / Shared / EditorTemplates中添加了Order.cshmtl模板(添加了“color”来验证我正在使用此模板):
- @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”?否则,该参数是什么?
解决方法
TL;DR > Named templates don’t work with collections,use a foreach loop to work around it – See below for extensive details about why,and an example.
你说:
Any idea why the EditorFor doesn’t use the template “OrderList” I
specified in the “templateName” argument? Otherwise,what is that
argument for?
EditorFor实际上是使用你指定的模板OrderList,但是你已经偶然发现了一些令人困惑的事情.一些研究提出了很多提示,但是我在这篇文章中发现了真正的坚果和细节:Problem with MVC EditorFor named template
简而言之,发生的是默认的情况:@ Html.EditorFor(Model => Model.Orders)实际上是按照惯例在临时调用MVC默认模板,但这根本不是很明显.
尝试这样思考:
在工作版本中,您将传递一个类型列表< Order>参考Model.Orders(MANY orders),但模板是用Order(single,NOT MANY)模型指定的.
有趣.为甚么甚至工作?乍看起来好像不行.但是,由于幕后发生了什么,它确实有效.
从上述帖子改写:
When you use
@Html.EditorFor(c => c.Orders)@H_403_60@ MVC convention chooses
the default template forIEnumerable@H_403_60@
. This template is part of the MVC framework,and what it does is generate
Html.EditorFor()@H_403_60@ for
each item in the enumeration. That template then generates the
appropriate editor template for each item in the list individually
– in your case they’re all instances ofOrder@H_403_60@,so,the
Order@H_403_60@ template is used for each item.
这是魔法,它是方便的,但是因为这是惯例发生的,并且基本上是我们隐藏的,所以我认为是混乱的根源.
现在当你尝试做同样的事情,但使用一个命名的模板,通过显式设置你的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")
- }
部分你的问题表示:
I was hoping I could use different child EditorTemplates for different
purposes with the same child collection.
您可以通过在循环中引入条件并在其中选择替代模板(无论是对于整个列表还是按顺序排列,仅取决于如何编写条件)
- @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")
- }
- }
对不起这么冗长希望有帮助.