Editor per IEnumerable con TemplateName

Supponiamo che io abbia un modello semplice per spiegare lo scopo:

public class Category { ... public IEnumerable Products { get; set; } } 

Vista:

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

EditorTemplate:

 @model Product ... 
  • @Html.EditorFor(m => m.Name)
  • Si noti che non è necessario definire EditorTemplate per IEnumerable , posso solo crearlo per il modello Product e MVC framework è abbastanza intelligente da utilizzare il proprio modello per IEnumerable. Esso scorre la mia collezione e chiama il mio EditorTemplate.

    L’output html sarà qualcosa di simile

     ... 
  • che posso postare sul mio controller, dopo tutto.

    Ma perché MVC non fa il trucco quando definisco EditorTemplate con un nome di modello?

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

    In tal caso devo cambiare il tipo di proprietà in IList , scorrere da solo la collezione e chiamare il EditorTemplate

     @for (int i = 0; i  m.Products[i], "ProductTemplate") } 

    che sembra una specie di soluzione sporca per me. C’è qualche altra soluzione più pulita per fare questo?

    C’è qualche altra soluzione più pulita per fare questo?

    La semplice risposta è no, fa schifo, sono completamente d’accordo con te, ma è così che i progettisti del framework hanno deciso di implementare questa funzionalità.

    Quindi quello che faccio è attenermi alle convenzioni. Poiché ho specifici modelli di vista per ogni vista e partial, non è un grosso problema avere un modello editor corrispondente, chiamato allo stesso modo del tipo della collezione.

    Ecco, ora devo solo 9999 birre Darin.

      public static MvcHtmlString EditorForMany(this HtmlHelper html, Expression>> 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>(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()); }