2012-10-09 6 views
7

In MVC è possibile creare un modello di editor per la T e poi quando si vuole rendere un editor per una proprietà di tipo IEnumerable<T> si può semplicemente fare per esempioCome creare un EditorTemplate utilizzando UIHint con una proprietà di tipo IEnumerable <T>

Html.EditorFor(m => m.MyListOfT) 

La bellezza di questo è che i nomi vengono creati automaticamente dal framework per gli ingressi, e poi durante la pubblicazione di nuovo il modello vincolante tutte le opere bene.

La mia domanda è: come si fa quanto sopra quando si dispone di più di un tipo di modello di editor?

Ho provato con UIHint(), ma sembra solo per permettere di specificare l'UIHint contro la lista, piuttosto che ogni elemento della lista. Ciò significa che devi quindi creare un EditorTemplate per l'elenco, con un ciclo foreach(), e quindi perdere la simpatica auto-denominazione e associazione dei modelli.

Cosa mi manca qui?

Il modello è per esempio

public class MyViewModel 
{ 
    public IEnumerable<SomeType> SomeProperty { get; set; } 
} 

Idealmente voglio fare qualcosa di simile:

public class MyViewModel 
{ 
    [UIHint("SomeTypeTemplate")] 
    public IEnumerable<SomeType> SomeProperty { get; set; } 
} 

e hanno che si applicano automaticamente a tutti gli elementi della lista in modo da poter rendere con solo:

Html.EditorFor(m => m.SomeProperty) 

risposta

10

Che cosa mi manca qui?

Niente. Sfortunatamente è così. Se si specifica il nome del modello quando si chiama il Html.EditorFor o utilizzando un UIHint il modello sarà chiamato per l'elenco e non per ogni elemento.

Detto questo si potrebbe, naturalmente, scrivere un metodo di estensione personalizzato che ottenere questa funzionalità:

public static class HtmlExtensions 
{ 
    private class ViewDataContainer: IViewDataContainer 
    { 
     public ViewDataContainer(ViewDataDictionary viewData) 
     { 
      ViewData = viewData; 
     } 

     public ViewDataDictionary ViewData { get; set; } 
    } 

    public static IHtmlString EditorForCollection<TModel, TProperty>(
     this HtmlHelper<TModel> html, 
     Expression<Func<TModel, IList<TProperty>>> expression 
    ) 
    { 
     var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); 
     if (string.IsNullOrEmpty(metadata.TemplateHint)) 
     { 
      return html.EditorFor(expression); 
     } 

     var collection = metadata.Model as IList<TProperty>; 

     var sb = new StringBuilder(); 
     for (int i = 0; i < collection.Count; i++) 
     { 
      var indexExpression = Expression.Constant(i, typeof(int)); 
      var itemGetter = expression.Body.Type.GetProperty("Item", new[] { typeof(int) }).GetGetMethod(); 
      var methodCallExpression = Expression.Call(expression.Body, itemGetter, indexExpression); 
      var itemExpression = Expression.Lambda<Func<TModel, TProperty>>(methodCallExpression, expression.Parameters[0]); 
      var result = html.EditorFor(itemExpression, metadata.TemplateHint).ToHtmlString(); 
      sb.AppendLine(result); 
     } 
     return new HtmlString(sb.ToString()); 
    } 
} 

che potrebbe operare sulle proprietà vista del modello di raccolta tipo che sono decorati con l'attributo UIHint:

public class MyViewModel 
{ 
    [UIHint("SomeTypeTemplate")] 
    public IList<ItemViewModel> Items { get; set; } 
} 

e nella vista:

@model MyViewModel 
@Html.EditorForCollection(x => x.Items) 

e y il nostro ~/Views/Shared/EditorTemplates/SomeTypeTemplate.cshtml potrebbe ora essere digitato ad un unico ItemViewModel:

@model ItemViewModel 
... 

non è più necessario un modello di visualizzazione intermediario in cui si sarebbe semplicemente loop e chiamando il modello attuale - che sarebbe un vero spreco.

+0

Eccellente :-) grazie Darin – magritte

2

Si crea 2 EditorTemplates. Uno gestisce l'IEnumerable, e questo modello di loop per creare un

Html.EditorFor(m => m.SomeProperty, "SomeIndividualTemplate") 

per ogni elemento nell'enumerazione.

1

Anche se la risposta di @ Darin è perfetta, per alcune ragioni che non ho ancora trovato il TemplateHint da ModelMetaData è sempre stato nullo. Come soluzione ho creato un altro sovraccarico del codice di @ Darin per accettare il parametro nome del modello e visualizzare la vista invece di cercare TemplateHint in ModelMetaData. Mi spiace di postarlo come risposta in quanto non ho privilegi da postare come commento. Grazie Darin :)

Problemi correlati