如何在包装 ViewModel 中对复杂类型使用 EditorForModel 和 DataAnnotations?

发布于 2024-12-25 06:22:03 字数 1356 浏览 2 评论 0原文

我有一个包含两种复杂类型的 ViewModel:

public class EditProductViewModel
{
    public ProductData ProductData { get; set; }
    public FridgeContent FridgeContent { get; set; }
}

并且此视图:

@model EditProductViewModel
@using (Html.BeginForm("Edit", "ProductData", FormMethod.Post))
{
   @Html.EditorForModel()
   [...]
}

ProductData 和 FridgeContent 包含带有 DataAnnotations 的 POCO 属性,如下所示:

public class FridgeContentMetadata : DatabaseEntityMetadataBase
{
    [Required]
    [HiddenInput(DisplayValue = false)]
    public int ProductDataId { get; set; }

    [Required]
    [UIHint("StringReadOnly")]
    public int ScaleId { get; set; }

    [Required]
    [UIHint("StringReadOnly")]
    [Range(0.01, float.MaxValue, ErrorMessage = "The weight of a product must be positive.")]
    public float Weight { get; set; }

    [...]
}

我想使用这些类和 EditorForModel() 方法中的适当数据注释来编辑 EditProductView 中的 ProductData 和 FridgeContent (我不想自己生成模板)。因此,我在 /Views/Shared/EditorTemplates/ 中创建了模板 ProductData.cshtml 和 FridgeContent.cshtml:

@model FridgeContent 
@Html.EditorForModel()

不幸的是,EditProductViewModel 的视图是空的(没有引发错误)。如果我单独将 EditorForModel 用于 FridgeContent 或 ProductData,则它工作正常。我还尝试向 EditProductViewModel 添加 [UIHInt("..")] 注释,但这没有什么区别。

我缺少什么?

I have a ViewModel wrapping two complex types:

public class EditProductViewModel
{
    public ProductData ProductData { get; set; }
    public FridgeContent FridgeContent { get; set; }
}

and this view:

@model EditProductViewModel
@using (Html.BeginForm("Edit", "ProductData", FormMethod.Post))
{
   @Html.EditorForModel()
   [...]
}

ProductData and FridgeContent contain POCO properties with DataAnnotations like this:

public class FridgeContentMetadata : DatabaseEntityMetadataBase
{
    [Required]
    [HiddenInput(DisplayValue = false)]
    public int ProductDataId { get; set; }

    [Required]
    [UIHint("StringReadOnly")]
    public int ScaleId { get; set; }

    [Required]
    [UIHint("StringReadOnly")]
    [Range(0.01, float.MaxValue, ErrorMessage = "The weight of a product must be positive.")]
    public float Weight { get; set; }

    [...]
}

I want to edit both ProductData and FridgeContent in the EditProductView using the appropriate data annotations from those classes and the EditorForModel() method (I don't want to generate the templates myself). I therefore created the templates ProductData.cshtml and FridgeContent.cshtml in /Views/Shared/EditorTemplates/:

@model FridgeContent 
@Html.EditorForModel()

Unfortunately, the view for EditProductViewModel is empty (no errors raised). If I use EditorForModel for either FridgeContent or ProductData alone, it's working fine. I also tried adding [UIHInt("..")] annotations to EditProductViewModel but that doesn't make a difference.

What am I missing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

养猫人 2025-01-01 06:22:03
@model EditProductViewModel
@using (Html.BeginForm("Edit", "ProductData", FormMethod.Post))
{
   @Html.EditorFor(o=> o.ProductData )

   @Html.EditorFor(o=> o.FridgeContent )
}

或者为你的ViewModel创建一个包含这两行的编辑模板

   @Html.EditorFor(o=> o.ProductData )
   @Html.EditorFor(o=> o.FridgeContent )

UPADTE:

哦终于明白了,因为渲染引擎不会在对象层次结构中走更多一步,你可以在asp.net mvc代码中找到它还。

检查MVC 3.0源代码这里

有一个名为DefaultEditorTemplates.cs的文件 其中包含此方法:

internal static string ObjectTemplate(HtmlHelper html, TemplateHelpers.TemplateHelperDelegate templateHelper) {
    ViewDataDictionary viewData = html.ViewContext.ViewData;
    TemplateInfo templateInfo = viewData.TemplateInfo;
    ModelMetadata modelMetadata = viewData.ModelMetadata;
    StringBuilder builder = new StringBuilder();

    if (templateInfo.TemplateDepth > 1) {    // DDB #224751
        return modelMetadata.Model == null ? modelMetadata.NullDisplayText : modelMetadata.SimpleDisplayText;
    }

    foreach (ModelMetadata propertyMetadata in modelMetadata.Properties.Where(pm => ShouldShow(pm, templateInfo))) {
        if (!propertyMetadata.HideSurroundingHtml) {
            string label = LabelExtensions.LabelHelper(html, propertyMetadata, propertyMetadata.PropertyName).ToHtmlString();
            if (!String.IsNullOrEmpty(label)) {
                builder.AppendFormat(CultureInfo.InvariantCulture, "<div class=\"editor-label\">{0}</div>\r\n", label);
            }

            builder.Append("<div class=\"editor-field\">");
        }

        builder.Append(templateHelper(html, propertyMetadata, propertyMetadata.PropertyName, null /* templateName */, DataBoundControlMode.Edit, null /* additionalViewData */));

        if (!propertyMetadata.HideSurroundingHtml) {
            builder.Append(" ");
            builder.Append(html.ValidationMessage(propertyMetadata.PropertyName));
            builder.Append("</div>\r\n");
        }
    }

    return builder.ToString();
}

它清楚地表明如果 TemplateDepth > 1 只是渲染一个简单的文本。

@model EditProductViewModel
@using (Html.BeginForm("Edit", "ProductData", FormMethod.Post))
{
   @Html.EditorFor(o=> o.ProductData )

   @Html.EditorFor(o=> o.FridgeContent )
}

or create an edit template for you ViewModel containing these two lines

   @Html.EditorFor(o=> o.ProductData )
   @Html.EditorFor(o=> o.FridgeContent )

UPADTE:

Oh got it finally because the rendering engine will not go more that one step in object hierarchy, you can find it in asp.net mvc code also.

Check the MVC 3.0 Source Code Here:

There is a file named DefaultEditorTemplates.cs which contains this method:

internal static string ObjectTemplate(HtmlHelper html, TemplateHelpers.TemplateHelperDelegate templateHelper) {
    ViewDataDictionary viewData = html.ViewContext.ViewData;
    TemplateInfo templateInfo = viewData.TemplateInfo;
    ModelMetadata modelMetadata = viewData.ModelMetadata;
    StringBuilder builder = new StringBuilder();

    if (templateInfo.TemplateDepth > 1) {    // DDB #224751
        return modelMetadata.Model == null ? modelMetadata.NullDisplayText : modelMetadata.SimpleDisplayText;
    }

    foreach (ModelMetadata propertyMetadata in modelMetadata.Properties.Where(pm => ShouldShow(pm, templateInfo))) {
        if (!propertyMetadata.HideSurroundingHtml) {
            string label = LabelExtensions.LabelHelper(html, propertyMetadata, propertyMetadata.PropertyName).ToHtmlString();
            if (!String.IsNullOrEmpty(label)) {
                builder.AppendFormat(CultureInfo.InvariantCulture, "<div class=\"editor-label\">{0}</div>\r\n", label);
            }

            builder.Append("<div class=\"editor-field\">");
        }

        builder.Append(templateHelper(html, propertyMetadata, propertyMetadata.PropertyName, null /* templateName */, DataBoundControlMode.Edit, null /* additionalViewData */));

        if (!propertyMetadata.HideSurroundingHtml) {
            builder.Append(" ");
            builder.Append(html.ValidationMessage(propertyMetadata.PropertyName));
            builder.Append("</div>\r\n");
        }
    }

    return builder.ToString();
}

which clearly states that if the TemplateDepth > 1 just render a simple text.

烟花易冷人易散 2025-01-01 06:22:03

正如上面的答案所示,这个问题似乎与限制它将考虑的嵌套深度的框架有关。

解决该问题的一种方法是使用您自己的编辑器模板。在 Views/Shared/EditorTemplates 中创建分部视图 Object.cshtml。以下是取自此处的示例模板:

@{
    Func<ModelMetadata, bool> ShouldShow = metadata =>
        metadata.ShowForEdit && !ViewData.TemplateInfo.Visited(metadata);
}
@if (ViewData.TemplateInfo.TemplateDepth > 5) {
    if (Model == null) { 
        @ViewData.ModelMetadata.NullDisplayText 
    } else { 
        @ViewData.ModelMetadata.SimpleDisplayText 
    } 
} else {     
    foreach (var prop in ViewData.ModelMetadata.Properties.Where(ShouldShow)) { 
        if (prop.HideSurroundingHtml) { 
            @Html.Editor(prop.PropertyName) 
        } else { 
            if (string.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString())==false) { 
                <div class="editor-label">
                    @Html.Label(prop.PropertyName) 
                </div>
            } 
            <div class="editor-field">
                @Html.Editor(prop.PropertyName)  
                @Html.ValidationMessage(prop.PropertyName) 
            </div>
        } 
    } 
} 

在上面的示例中,您可以通过以下方式设置最大嵌套深度:更改 5 常量。

As the above answer shows, this problem seems related to the framework limiting the depth of nesting it will consider.

One way to work around the problem is to use your own editor template. Create the partial view, Object.cshtml, in Views/Shared/EditorTemplates. Here's an example template taken from here:

@{
    Func<ModelMetadata, bool> ShouldShow = metadata =>
        metadata.ShowForEdit && !ViewData.TemplateInfo.Visited(metadata);
}
@if (ViewData.TemplateInfo.TemplateDepth > 5) {
    if (Model == null) { 
        @ViewData.ModelMetadata.NullDisplayText 
    } else { 
        @ViewData.ModelMetadata.SimpleDisplayText 
    } 
} else {     
    foreach (var prop in ViewData.ModelMetadata.Properties.Where(ShouldShow)) { 
        if (prop.HideSurroundingHtml) { 
            @Html.Editor(prop.PropertyName) 
        } else { 
            if (string.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString())==false) { 
                <div class="editor-label">
                    @Html.Label(prop.PropertyName) 
                </div>
            } 
            <div class="editor-field">
                @Html.Editor(prop.PropertyName)  
                @Html.ValidationMessage(prop.PropertyName) 
            </div>
        } 
    } 
} 

In the above example, you can set the maximum nesting depth by changing the 5 constant.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文