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);
}

顧客 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' のモデル アイテムが必要です。」</p>

「templateName」引数で指定したテンプレート「OrderList」を EditorFor が使用しない理由がわかりませんか?それ以外の場合、その引数は何のためのものですか?

4

1 に答える 1

26

TL;DR > 名前付きテンプレートはコレクションでは機能しません。foreach ループを使用して回避してください。理由と例の詳細については、以下を参照してください。


あなたが言った:

「templateName」引数で指定したテンプレート「OrderList」を EditorFor が使用しない理由がわかりませんか?それ以外の場合、その引数は何のためにあるのでしょうか?

EditorForOrderListあなたが指定したテンプレートを実際に使用していますが、非常に紛らわしいものに出くわしました。いくつかの調査で多くのヒントが得られましたが、この投稿で実際の基本的な詳細を見つけました:名前付きテンプレートの MVC エディターの問題

要するに、何が起こっているかというと、@Html.EditorFor(Model=>Model.Orders)実際に機能するデフォルトのケースは、慣例により暫定的にMVCのデフォルトテンプレートを呼び出しているということですが、これはまったく明らかではありません。

次のように考えてみてください。

List<Order>作業バージョンでは、(MANY 注文)への参照を含む型を渡していますModel.Ordersが、テンプレートはOrder(single, NOT MANY) のモデルで指定されています。

面白い。なぜそれが機能するのですか?一見、うまくいかないように見えます。しかし、舞台裏で何が起こっているかによって、それは機能します。

上記の投稿からの言い換え:

@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!!!

エラーを解消する方法はいくつかありますが、それらの多くは、POST でインデックスによって個々のコントロールをアドレス指定できない方法で HTML コントロールがレンダリングされるため、問題があります。うーん。(注: 実際のケースでは、期待どおりに 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")
    }
} 

申し訳ありませんが冗長です。それが役立つことを願っています。

于 2013-05-19T04:40:27.590 に答える