ASP.NET MVC editor template for property

后端 未结 2 1528
余生分开走
余生分开走 2021-02-19 22:51

Usually I render my forms by @Html.RenderModel, but this time I have a complex rendering logic and I render it manually. I decided to create a editor template for one property.

相关标签:
2条回答
  • 2021-02-19 22:56

    What type is the "x.SomeProperty"? I'm gonna assume for now it's type is called Property.

    MyModel.cs

    public class MyModel
    {
        public Property SomeProperty { get; set; }
    }
    

    Property.cs

    public class Property
    {
        [Display(Name="Foo")]
        public int Value { get; set; }
    }
    

    Views/Shared/EditorTemplates/Property.cshtml

    @model MvcApplication1.Models.Property
    
    @Html.LabelFor(model => model.Value)
    @Html.EditorFor(model => model.Value)
    @Html.ValidationMessageFor(model => model.Value)
    

    MyView.cshtml

    @Html.EditorFor(model=>model.SomeProperty)
    

    If you don't provide templatename for EditorFor helper it finds editortemplate which name matches SomeProperty's type.

    Update:

    To make a custom editortemplate for string you do:

    Model

    public class MyModel
    {
        public string SomeProperty { get; set; }
    }
    

    View:

    @Html.EditorFor(model => model.SomeProperty,"Property")
    

    Or alternatively:

    Model:

    public class MyModel
    {
        [DataType("Property")]
        public string SomeProperty { get; set; }
    }
    

    View:

    @Html.EditorFor(model => model.SomeProperty) 
    

    Views/Shared/EditorTemplates/Property.cshtml:

    @model string
    
    @Html.Raw("I'm using property editortemplate:")
    @Html.Label(ViewData.ModelMetadata.PropertyName)
    @Html.Editor(ViewData.ModelMetadata.PropertyName)
    @Html.ValidationMessage(ViewData.ModelMetadata.PropertyName)
    
    0 讨论(0)
  • 2021-02-19 23:08

    You are calling editor from your editor. As @RPM1984 rephrases @darin-dmitrov in comment in this answer: You can only have 1 template used at runtime for a given type, in a given Views particular context.

    If you change your view to render textbox instead of editor, it works, I just tried:

    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
    <% var modelMetadata = ViewData.ModelMetadata; %>
    <% if (modelMetadata.HideSurroundingHtml)
       { %>
       <%= Html.Editor(modelMetadata.PropertyName) %>
    <% }
       else
       { %>
       <% if (!String.IsNullOrEmpty(modelMetadata.DisplayName))
           { %>
           <div class="editor-label"><%= Html.Label(modelMetadata.PropertyName) %></div>
        <% } %>
        <div class="editor-field"><%= Html.TextBox(modelMetadata.PropertyName) %> <%= Html.ValidationMessage(modelMetadata.PropertyName) %></div>
    <% } %>
    

    If you want to render something else instead of textbox (i.e. dropdown list), you need to decide that inside your template for that property and render it. Or, if you have something common for more editors, I usually extract that into partial view in Shared folder, and just use Html.Partial("ViewName")

    And, regarding labels are rendered regardless of DisplayName, to prevent label from rendering if there is no display name, change your if condition to !String.IsNullOrEmpty(modelMetadata.DisplayName) (I already put it that way in main code block)

    EDIT This edit refers to question related to object.ascx default editor template. This is code of object.ascx, taken from Brad Wilson's blog:

    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
    <% if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
        <%= ViewData.ModelMetadata.SimpleDisplayText%>
    <% }
       else { %>    
        <% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit
                             && !ViewData.TemplateInfo.Visited(pm))) { %>
            <% if (prop.HideSurroundingHtml) { %>
                <%= Html.Editor(prop.PropertyName) %>
            <% }
               else { %>
                <% if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString())) { %>
                    <div class="editor-label"><%= Html.Label(prop.PropertyName) %></div>
                <% } %>
                <div class="editor-field">
                    <%= Html.Editor(prop.PropertyName) %>
                    <%= Html.ValidationMessage(prop.PropertyName, "*") %>
                </div>
            <% } %>
        <% } %>
    <% } %>
    

    This code indeed calls Html.Editor from inside of editor, but inside a loop which creates list of editors for properties of complex model. Each of these calls will invoke corresponding editor (i.e for string it will show string.ascx there etc), and only if you have some "unknown" property which is not string and there is no specific editor for it (i.e. byte[]) it will invoke object.ascx for it, but this is NOT calling editor for the current property (what you are trying to do):

    The Object template’s primary responsibility is displaying all the properties of a complex object, along with labels for each property. However, it’s also responsible for showing the value of the model’s NullDisplayText if it’s null, and it’s also responsible for ensuring that you only show one level of properties (also known as a “shallow dive” of an object). In the next blog post, we’ll talk about ways to customize this template, including performing “deep dive” operations.


    SUMMARY

    Short version:

    More editors for same property is basically solution for functional differences ("for yes/no I want here radio group and there drop-down)", and for visual differences partial views should be used, as you can nest them as much as you want, because you explicitly call them by name so there is no limits imposed, you are responsible to prevent any potential recursions.

    Long version:

    I have been investigating this, as I have the same problem, I'm using editor template to render <li> or <td> element (depending on config/theme) and from inside it call another editor which contains label and input (same for both cases, but if property is bool then input is before label), where I'm again calling third template for input (to prevent duplicating code for label/input and input/label scenarios), but this does not work. Although I did not found explanation on msdn or other relevant source, I figured out that the only scenario when editor gives nothing is when you want to render editor for the property which is the context of current editor (so it is actually exactly what I already quoted: "You can only have 1 template used at runtime for a given type, in a given Views particular context."). After thinking some more about this, I believe now that they are right in imposing this limit, as property x can only be rendered using one editor. You can have as many editors for property x as you want, but you cannot render one property once using more than one template. Any of your templates for rendering property x can use other templates to render PARTS of property x, but you cannot use (same or different) editor for x more than once (the same logic applies as to having two or more properties x (same type and name) on same model).

    Besides, if you could insert another template for current property into current template, that enables chaining any number of templates for current property, and can easily cause recursion, so one way or another it will lead you to stackoverflow :)

    0 讨论(0)
提交回复
热议问题