一个模型可以通过多个编辑器模板传递吗?

时间:2011-05-31 19:02:44

标签: asp.net-mvc razor

我正在尝试使用编辑器模板显示视图模型,该模板在应用基础对象编辑器模板之前将模型包装在字段集中。

我的观点:

@model Mvc3VanillaApplication.Models.ContactModel

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

使用字段集模板(Views / Shared / 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; }
    }
}

2 个答案:

答案 0 :(得分:5)

我实施了你所拥有的,并且能够重现它。我在Object.cshtml中设置了一个断点,所以我可以检查它,并且当我使用fieldset模板时,我发现它甚至没有碰到对象模板。然后我逐步完成了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 */));
}

显然不是这样,所以我在F12上按了TemplateHelpers.TemplateHelper,再次在那里我按F12进行单行调用,这会将你带到函数的内容。在这里,我从TemplateHelpers.cs的第214行开始发现这段短代码:

// 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;
}

这些评论实际上在代码中,我们在这里得到了您的问题的答案:一个模型可以通过多个编辑器模板传递吗?,答案是否*

话虽这么说,这似乎是一个非常合理的用例,所以找到一个替代方案可能是值得的。我怀疑一个模板化的剃刀委托会解决这个包装功能,所以我试了一下。

@{
    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版本,但它可能比它的价值更麻烦。当我们发现Html.ActionLink函数在定义了几百条路径时速度很慢时,我们尝试在careers项目上执行此操作。其余的库存在签名问题,我们认为现在不值得花时间研究并维护MVC的未来版本。

答案 1 :(得分:2)

在第一个cshtml模板中,我们可以重新创建ViewData.TemplateInfo(并清除VisitedObjects列表)

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

现在我们可以调用具有相同模型的另一个模板

@Html.DisplayForModel("SecondTemplate")