2012-10-09 6 views
7

In MVC Sie eine Editor Vorlage für T erstellen können und dann, wenn Sie einen Editor für eine Eigenschaft des Typs machen wollen IEnumerable<T> Sie einfach zum Beispiel tun könnenWie ein EditorTemplate erstellen mit UIHint mit einer Eigenschaft vom Typ IEnumerable <T>

Html.EditorFor(m => m.MyListOfT) 

Das Schöne daran ist, dass Namen werden automatisch durch den Rahmen für die Eingänge angelegt, und dann, wenn das Modell gut alle Arbeiten verbindlich Entsendung zurück.

Meine Frage ist: Wie machen Sie das oben, wenn Sie mehr als eine Art von Editor-Vorlage haben?

Ich habe versucht mit UIHint(), aber es scheint nur Ihnen zu erlauben, die UIHint gegen die Liste, anstatt jedes Element in der Liste anzugeben. Das bedeutet, dass Sie dann ein EditorTemplate für die Liste erstellen müssen, mit einer foreach() - Schleife, und dann verpassen Sie die nette automatische Benennung und Modellbindung.

Was fehlt mir hier?

Das Modell ist z.B.

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

Idealerweise möchte ich etwas tun:

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

und haben diese automatisch auf alle Elemente in der Liste anwenden, so kann ich mit nur machen:

Html.EditorFor(m => m.SomeProperty) 

Antwort

10

Was fehlt mir hier?

Nichts. Leider ist es so. Wenn Sie beim Aufruf von Html.EditorFor den Namen der Vorlage angeben oder eine UIHint verwenden, wird die Vorlage für die Liste und nicht für jedes Element aufgerufen.

Vor diesem Hintergrund sei man könnte natürlich eine benutzerdefinierte Erweiterungsmethode schreiben, die diese Funktionalität erreichen werden:

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

, die auf Sicht Modell-Eigenschaften des Typs Sammlung arbeiten könnte, die mit dem UIHint-Attribut versehen sind:

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

und in der Ansicht:

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

und y unsere ~/Views/Shared/EditorTemplates/SomeTypeTemplate.cshtml jetzt zu einem einzigen ItemViewModel eingegeben werden könnten:

@model ItemViewModel 
... 

Sie müssen nicht mehr eine vermittelnde Anzeigevorlage, in dem würden Sie einfach die tatsächliche Vorlage werden Looping und ruft - die eine echte Verschwendung wäre.

+0

Hervorragend :-) Danke daran – magritte

2

Sie erstellen 2 EditorTemplates. One behandelt das IEnumerable, und diese Vorlage wird für jedes Element in der Enumeration in eine Schleife umgewandelt, um ein zu erstellen.

1

Obwohl @ Darins Antwort perfekt ist, ist aus bestimmten Gründen, die ich finde, der TemplateHint aus ModelMetaData immer Null. Als Lösung habe ich eine weitere Überladung von @ Darins Code erstellt, um den Template-Namen-Parameter zu akzeptieren und die View rendern zu lassen, anstatt nach TemplateHint in ModelMetaData zu suchen. Tut mir leid, es als Antwort zu posten, da ich keine Privilegien habe, um als Kommentar zu posten. Danke Darin :)

Verwandte Themen