2011-05-31 83 views
6

我想在應用基礎對象編輯器模板之前使用將模型包裝到字段集中的編輯器模板來顯示視圖模型。一個模型可以通過多個編輯器模板傳遞嗎?

我的觀點:

@model Mvc3VanillaApplication.Models.ContactModel 

@using (Html.BeginForm()) 
{ 
    @Html.EditorForModel("Fieldset") 
} 

使用一個字段集模板(查看/共享/ EditorTemplates/Fieldset.cshtml):

<fieldset> 
    <legend>@ViewData.ModelMetadata.DisplayName</legend> 
    @Html.EditorForModel() 
</fieldset> 

又使用一個基本的模板對於所有對象(Views/Shared/EditorTemplates/Object.cshtml):

@foreach (var prop in ViewData.ModelMetadata.Properties.Where(x => 
    x.ShowForEdit && !x.IsComplexType && !ViewData.TemplateInfo.Visited(x))) 
{ 
    @Html.Label(prop.PropertyName, prop.DisplayName) 
    @Html.Editor(prop.PropertyName) 
} 

無論如何,這是我的意圖。問題是,當頁面使用字段集和圖例進行渲染時,不應用「對象」模板,因此不顯示輸入控件。

如果我將視圖更改爲不指定「Fieldset」模板,那麼我的模型的屬性將使用Object模板呈現,所以這不是我的Object模板找不到。

是否可以通過多個模板傳遞相同的模型?

對於它的價值,視圖模型是這樣的:

namespace Mvc3VanillaApplication.Models 
{ 
    [System.ComponentModel.DisplayName("Contact Info")] 
    public class ContactModel 
    { 
     public string FirstName { get; set; } 
     public string LastName { get; set; } 
    } 
} 
+0

那麼複雜。你能發佈你的視圖模型代碼嗎?所以我們可以更清楚地瞭解你的問題。據我所知,你試圖爲所有編輯製作一個通用組件,對嗎? – thangchung 2011-06-01 14:54:14

+0

@ThangChung嗯,我重寫了對象編輯器模板,因爲它很沉重(將所有東西都封裝在div中),我想擴展它以適應其他一些慣例。除此之外,我使用fieldset模板,以便每個模型都顯示在fieldset中,該fieldset通過javascript成爲嚮導中的頁面。 – 2011-06-01 15:07:54

回答

5

我實現你所擁有的,並能夠重現。我在Object.cshtml中設置了一個斷點,所以我可以檢查它,並且當使用字段集模板時,我意識到它甚至沒有擊中對象模板。然後,我瀏覽了fieldset模板,看到它正在調用模板,所以必須在代碼中發生一些事情,以防止它顯示對象模板。

我打開了MVC3 source code,搜索了EditorForModel並找到了正確的功能。

public static MvcHtmlString EditorForModel(this HtmlHelper html) { 
    return MvcHtmlString.Create(TemplateHelpers.TemplateHelper(html, html.ViewData.ModelMetadata, String.Empty, null /* templateName */, DataBoundControlMode.Edit, null /* additionalViewData */)); 
} 

顯然,這是不是,所以我就TemplateHelpers.TemplateHelper按下F12,一旦再次出現在我一行調用,它帶給你的函數的肉壓F12。在這裏,我發現這個代碼的短位開始上線214 TemplateHelpers.cs

// Normally this shouldn't happen, unless someone writes their own custom Object templates which 
// don't check to make sure that the object hasn't already been displayed 
object visitedObjectsKey = metadata.Model ?? metadata.RealModelType; 
if (html.ViewDataContainer.ViewData.TemplateInfo.VisitedObjects.Contains(visitedObjectsKey)) { // DDB #224750 
    return String.Empty; 
} 

這些評論實際上是在代碼中,在這裏,我們有回答你的問題:可以在一個模型可以通過多個編輯模板傳遞?,答案是no *。這就是說,這似乎是一個非常合理的用例,因此尋找替代品可能是值得的。我懷疑一個模板化的剃鬚刀代表會解決這個包裝功能,所以我試了一下。

@{ 
    Func<dynamic, object> fieldset = @<fieldset><legend>@ViewData.ModelMetadata.DisplayName</legend>@Html.EditorForModel()</fieldset>; 
} 

@using (Html.BeginForm()) 
{ 
    //@Html.EditorForModel("Fieldset") 
    //@Html.EditorForModel() 
    @fieldset(Model) 
} 

和中提琴!有效!我將留給你來實現這個作爲擴展(以及更多可重用)的方法。這裏有一篇關於templated razor delegates的簡短博文。


*從技術上講,你可以重寫這個函數並編譯你自己的MVC3版本,但它可能比它的價值更麻煩。我們試圖在careers項目上這樣做,當我們發現Html.ActionLink函數在定義幾百條路由時非常緩慢。與其他圖書館簽署了一個簽名問題,我們認爲這不值得我們花時間完成現在的工作,併爲將來的MVC版本進行維護。

+1

感謝您的徹底解答;擴展方法似乎是要走的路。我查看了源代碼並注意到(TemplateInfo.cs:33)添加了VisitedObjects HashSet以防止無限遞歸。看起來,如果MVC團隊使用模型和模板組合而不是模型,它可能會更好。這樣它仍然會阻止遞歸,但允許應用多個模板。然而,我會遠離自己的MVC版本。 – 2011-06-02 13:04:25

+0

@EricBock:另一種選擇可能是將模型封裝在只有1個屬性(一個對象)的「FieldsetViewModel」中。這似乎有點麻煩,但。 – 2011-06-02 14:13:46

0

在第一CSHTML模板中,我們可以重新ViewData.TemplateInfo(和明確VisitedObjects列表)

var templateInfo = ViewData.TemplateInfo; 
ViewData.TemplateInfo = new TemplateInfo 
{ 
    HtmlFieldPrefix = templateInfo.HtmlFieldPrefix, 
    FormattedModelValue = templateInfo.FormattedModelValue 
}; 

現在我們可以調用另一個模板與同型號

@Html.DisplayForModel("SecondTemplate") 
相關問題