Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
228 views
in Technique[技术] by (71.8m points)

c# - EditorFor IEnumerable<T> with TemplateName

Suppose I have a simple model to explain the purpose:

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

View:

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

EditorTemplate:

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

Note that I don't have to define the EditorTemplate for IEnumerable<Product>, I can only create it for the Product model and MVC framework is smart enough to use its own template for IEnumerable. It iterates through my collection and calls my EditorTemplate.

The output html will be something like this

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

which I can post to my controller after all.

But why doesn't the MVC do the trick when I define EditorTemplate with a template name?

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

In that case I have to change the type of the property to IList<Product>, iterate through the collection by myself and call the EditorTemplate

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

which seems kind of dirty workaround to me. Is it any other, cleaner solution to do this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There, now I only owe Darin 9999 beers.

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...