2011-05-31 38 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") 
相关问题