在EditorFor中用于子对象时,MVC无法覆盖EditorTemplate名称

时间:2013-05-18 22:54:18

标签: asp.net-mvc mvc-editor-templates

我正在尝试使用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”?否则,该参数是什么?

1 个答案:

答案 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")
    }
} 

抱歉这么详细。希望有所帮助。