2012-12-26 24 views
18

假設我有一個簡單的模型來解釋的目的:EditorFor IEnumerable的<T>與TEMPLATENAME

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

查看:

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

EditorTemplate:

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

請注意,我不必須爲IEnumerable<Product>定義EditorTemplate,我只能爲創建它模型和MVC框架足夠聰明,可以使用IEnumerable自己的模板。它遍歷我的集合並調用我的EditorTemplate。

輸出的HTML將是這樣的

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

,我可以畢竟張貼到我控制器。

但是爲什麼當我使用模板名稱定義EditorTemplate時,MVC不會訣竅?

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

在這種情況下,我必須爲屬性的類型更改爲​​,我自己通過收集迭代,並調用EditorTemplate

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

這似乎有點髒的解決方法給我。這是乾淨的解決方案嗎?

+0

可能重複http://stackoverflow.com/questions/25333332/correct-idiomatic-way-to-use -custom-editor-templates-with-ienumerable-models-in) – GSerg

+0

不知道我在創建[那個問題]之前搜索時錯過了這個問題(http://stackoverflow.com/questions/25333332/correct-idiomatic-way- )使用自定義編輯器模板與ienumerable-models-in)。按照時間順序,我的問題應該作爲這個問題的重複來解決,但我相信它應該是相反的,因爲畢竟有一個解決方法。 – GSerg

+2

@GSerg,感謝您分享您的答案。我不能說我喜歡任何建議的解決方法,而不是通過集合中的簡單'for'循環。對不起,我不清楚我的問題,但我沒有尋找這個問題的解決方法,但對於乾淨和(我希望)唯一正確的方式來實現這一點。這就是爲什麼我不能說你的問題的答案解決了我的問題,儘管問題本身非常相似。 – Zabavsky

回答

13

難道還有其他更乾淨的解決方案嗎?

簡單的答案是否定的,它吸收不好,我完全同意你的觀點,但是這就是框架的設計者決定如何實現此功能。

所以我所做的是我堅持公約。由於我對每個視圖和局部視圖都有特定的視圖模型,因此擁有相應的編輯器模板並不是什麼大事,它們的命名方式與集合的類型相同。

+0

我很害怕。問題是我必須對一些相關視圖使用相同的ViewModel,並且取決於View如何顯示此ViewModel。但是,感謝達林,爲了答案,我很感激。 – Zabavsky

15

那裏,現在我只欠達林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()); 
    } 
的[正確的,慣用的方式使用在ASP.NET MVC的IEnumerable車型自定義編輯模板(
+1

你可以添加一個關於如何去使用它的例子嗎? –

+0

檢索html.ViewData.Model時,我得到一個空引用異常。任何線索? –

+1

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

相關問題