asp.net-mvc – 在EditorFor for child对象中使用时,MVC无法覆盖EditorTemplate的名称

我试图使用EditorTemplate在父视图中的表中显示一个子集合.我遇到的问题是,如果模板的命名与孩子的类完全相同,这只会起作用.当我尝试使用稍有不同名称的模板,并将该名称作为TemplateFor参数传递给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,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) MVC convention chooses
the default template for IEnumerable. This template is part of the MVC framework,and what it does is generate Html.EditorFor() 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 of Order,so,the Order 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")
    }
}

对不起这么冗长希望有帮助.

相关文章

判断URL文件是不是在于在。private static bool UrlIsExist(...
由于在.net中,Request时出现有HTML或Javascript等字符串时,...
public static bool ProcessIdCard(this string idCard, out...
protected void Page_Load(object sender, EventArgs e){ Sc...