2012-12-26 34 views
18

Supponiamo che io sono un modello semplice per spiegare lo scopo:EditorFor IEnumerable <T> con TemplateName

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

Vista:

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

EditorTemplate:

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

Nota che non lo faccio devi definire l'EditorTemplate per IEnumerable<Product>, posso solo crearlo per ilIl modelloe il framework MVC sono abbastanza intelligenti da utilizzare il proprio modello per IEnumerable. Esso scorre la mia collezione e chiama il mio EditorTemplate.

L'html output sarà simile a questo

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

cui posso inviare al mio controller, dopo tutto.

Ma perché MVC non risolve il problema quando definisco EditorTemplate con un nome di modello?

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

In questo caso devo cambiare il tipo di proprietà per IList<Product>, scorrere l'insieme da me e chiamare l'EditorTemplate

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

che sembra tipo di soluzione sporco per me. C'è qualche altra soluzione più pulita per fare questo?

+0

possibile duplicato di [modo idiomatico corretto utilizzare Editor modelli personalizzati con modelli IEnumerable in ASP.NET MVC] (http://stackoverflow.com/questions/25333332/correct-idiomatic-way-to-use -custom-editor-templates-with-ienumerable-models-in) – GSerg

+0

Non sono sicuro di come ho perso questa domanda durante la ricerca prima di creare [quella domanda] (http://stackoverflow.com/questions/25333332/correct-idiomatic-way- to-use-custom-editor-templates-with-ienumerable-models-in) dei miei. Cronologicamente la mia domanda dovrebbe essere chiusa come duplicato di questa, ma credo che dovrebbe essere il contrario, perché dopo tutto c'è una soluzione. – GSerg

+2

@GSerg, grazie per aver condiviso la tua risposta. Non posso dire che mi piacciono le soluzioni alternative suggerite meglio di un semplice ciclo 'for' attraverso la collezione. Ci scusiamo per non essere chiari con la mia domanda, ma non stavo cercando le soluzioni alternative a questo problema, ma per il pulito e (speravo) l'unico modo giusto per realizzare questo. Ecco perché non posso dire che le risposte alla tua domanda abbiano risolto il mio problema, anche se le domande stesse sono molto simili. – Zabavsky

risposta

13

C'è qualche altra soluzione più pulita per farlo?

La semplice risposta è no, fa schifo male, sono completamente d'accordo con te, ma è così che i progettisti del quadro hanno deciso di implementare questa funzione.

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.

+0

di cui avevo paura.Il problema è che devo usare lo stesso ViewModel per alcune viste correlate e dipende dalla vista su come visualizzare questo ViewModel. Ma grazie, Darin, per la risposta, lo apprezzo. – Zabavsky

15

Lì, ora devo solo le birre Darin 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()); 
    } 
+1

Puoi aggiungere un esempio su come utilizzeresti questo? –

+0

Ricevo un'eccezione di riferimento null durante il recupero di html.ViewData.Model. Qualche indizio? –

+1

'@ Html.EditorForMany (x => Model.Products)' –

Problemi correlati