21

目的を説明する単純なモデルがあるとします。

public class Category
{
    ...
    public IEnumerable<Product> Products { get; set; }
}

意見:

@model Category
...
<ul>
    @Html.EditorFor(m => m.Products)
</ul>

エディター テンプレート:

@model Product
...
<li>
    @Html.EditorFor(m => m.Name)
</li>

の EditorTemplate を定義する必要がないことに注意してください。モデルIEnumerable<Product>用にしか作成できずProduct、MVC フレームワークは IEnumerable 用に独自のテンプレートを使用できるほどスマートです。コレクションを反復処理し、EditorTemplate を呼び出します。

出力htmlは次のようになります

...
<li>
    <input id="Products_i_Name" name="Products[i].Name" type="text" value="SomeName">
</li>

結局、コントローラーに投稿できます。

しかし、テンプレート名を使用して EditorTemplate を定義すると、MVC がうまくいかないのはなぜでしょうか?

@Html.EditorFor(m => m.Products, "ProductTemplate")

その場合、プロパティのタイプを に変更し、IList<Product>自分でコレクションを反復処理して、EditorTemplate を呼び出す必要があります。

@for (int i = 0; i < Model.Products.Count; i++)
{
    @Html.EditorFor(m => m.Products[i], "ProductTemplate")
}

これは私には汚い回避策のようです。これを行うための他のよりクリーンなソリューションはありますか?

4

3 に答える 3

16

そこで、今ではダーリン 9999 ビールしか借りていません。

    public static MvcHtmlString EditorForMany<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TValue>>> expression, string templateName = null) where TModel : class
    {
        StringBuilder sb = new StringBuilder();

        // Get the items from ViewData
        var items = expression.Compile()(html.ViewData.Model);
        var fieldName = ExpressionHelper.GetExpressionText(expression);
        var htmlFieldPrefix = html.ViewContext.ViewData.TemplateInfo.HtmlFieldPrefix;
        var fullHtmlFieldPrefix = String.IsNullOrEmpty(htmlFieldPrefix) ? fieldName : String.Format("{0}.{1}", htmlFieldPrefix, fieldName);
        int index = 0;

        foreach (TValue item in items)
        {
            // Much gratitude to Matt Hidinger for getting the singleItemExpression.
            // Current html.DisplayFor() throws exception if the expression is anything that isn't a "MemberAccessExpression"
            // So we have to trick it and place the item into a dummy wrapper and access the item through a Property
            var dummy = new { Item = item };

            // Get the actual item by accessing the "Item" property from our dummy class
            var memberExpression = Expression.MakeMemberAccess(Expression.Constant(dummy), dummy.GetType().GetProperty("Item"));

            // Create a lambda expression passing the MemberExpression to access the "Item" property and the expression params
            var singleItemExpression = Expression.Lambda<Func<TModel, TValue>>(memberExpression,
                                                                               expression.Parameters);

            // Now when the form collection is submitted, the default model binder will be able to bind it exactly as it was.
            var itemFieldName = String.Format("{0}[{1}]", fullHtmlFieldPrefix, index++);
            string singleItemHtml = html.EditorFor(singleItemExpression, templateName, itemFieldName).ToString();
            sb.AppendFormat(singleItemHtml);
        }

        return new MvcHtmlString(sb.ToString());
    }
于 2013-09-24T06:56:28.730 に答える
14

これを行うための他のよりクリーンなソリューションはありますか?

簡単な答えはいいえです。それはひどいです。私はあなたに完全に同意しますが、それがフレームワークの設計者がこの機能を実装することを決定した方法です。

だから私がしていることは、慣習に固執することです。ビューとパーシャルごとに特定のビュー モデルがあるので、コレクションのタイプと同じ名前の、対応するエディター テンプレートを用意することは大したことではありません。

于 2012-12-26T09:27:39.357 に答える