12

我正在尝试使用 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);
}

客户索引.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'类型的模型项。”</p>

知道为什么 EditorFor 不使用我在“templateName”参数中指定的模板“OrderList”吗?否则,那个参数是干什么用的?

4

1 回答 1

26

TL;DR > 命名模板不适用于集合,请使用 foreach 循环来解决它 - 有关原因的详细信息和示例,请参见下文。


你说:

知道为什么 EditorFor 不使用我在“templateName”参数中指定的模板“OrderList”吗?否则,那个参数是干什么用的?

EditorFor实际上是在使用OrderList您指定的模板——但是您偶然发现了一些非常令人困惑的东西。一些研究发现了很多提示,但我在这篇文章中找到了真正的具体细节:MVC EditorFor 命名模板的问题

简而言之,发生的事情是默认情况下起作用:@Html.EditorFor(Model=>Model.Orders)实际上是按照约定在中间调用 MVC 默认模板,但这一点都不明显。

试着这样想:

在工作版本中,您传入一个List<Order>引用Model.Orders(许多订单)的类型,但模板是用Order(单个,不是很多)的模型指定的。

有趣的。为什么这甚至行得通?乍一看,它似乎不应该工作。但它确实有效,因为幕后发生的事情。

从上面提到的帖子中转述:

当您使用@Html.EditorFor(c => c.Orders)MVC 约定 时,为IEnumerable. 此模板是 MVC 框架的一部分,它的作用是Html.EditorFor()为枚举中的每个项目生成。然后,该模板为列表中的每个项目单独生成适当的编辑器模板 -在您的情况下,它们都是 的实例Order,因此,该Order模板用于每个项目。

这就是魔法,而且很方便,但是因为这是按照惯例发生的,而且基本上对我们来说是隐藏的,所以在我看来,这是造成混乱的根源。

现在,当您尝试做同样的事情但通过显式设置您使用特定编辑器模板来使用命名模板时,您最终会将该编辑器模板传递给整个枚举 - 这就是您发布的错误的来源。 EditorForOrderList

换句话说,失败的案例设法跳过了工作案例的“神奇”部分,这就是它失败的原因。但是,从语义上看,它看起来很好听,对吧?有混乱。

工作案例:

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 上的索引来处理各个控件。呃。(注意:工作案例确实按预期正确呈现 HTML)

要正确呈现 HTML 控件,您似乎必须使用常规for循环(不是 a 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")
    }
} 

抱歉太啰嗦了。希望有帮助。

于 2013-05-19T04:40:27.590 回答