Can one model go through several editor templates?

I am trying to display a view model using an editor template that wraps the model in a set of fields before applying the base object editor template.

My opinion:

@model Mvc3VanillaApplication.Models.ContactModel @using (Html.BeginForm()) { @Html.EditorForModel("Fieldset") } 

Uses a field set template (Views / Shared / EditorTemplates / Fieldset.cshtml):

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

Which in turn uses the basic template for all objects (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) } 

That is my intention anyway. The problem is that while the page is displayed using a set of fields and a legend, the object template is not applied, so input controls are not displayed.

If I changed the view so as not to specify the "Fieldset" template, then my model properties are displayed using the Object template, so my object template cannot be found.

Is it possible to transfer the same model through several templates?

For what it's worth, the presentation model is as follows:

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

I implemented what you have and was able to reproduce it. I set a breakpoint in Object.cshtml so that I can inspect it, and I was taken by surprise to realize that it didn't even hit the object template when using the field set template. Then I went through the fieldset template and saw that it just calls the template, so something should happen in the code that prevents it from displaying the object template.

I opened the source code of MVC3 , searched for EditorForModel and found the correct function.

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

Obviously, this was not the case, so I pressed F12 on TemplateHelpers.TemplateHelper , and as soon as I pressed F12 again on a single-line call, which brings you to the meat function. Here I found this short code code starting at line 214 of 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; } 

These comments are actually in the code, and here we have the answer to your question: Can one model be transmitted through several editor templates? , the answer is no * .

So this seems like a very reasonable option for such a feature, so finding an alternative is probably worth it. I suspected that the razor template delegate would solve this packing function, so I tried it.

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

And viola! It worked! I will leave it for you to implement this as a way to expand (and much more reusable). Here's a short blog post about razor template delegates .


* Technically, you can rewrite this function and compile your version of MVC3, but this is probably more of a problem than it's worth. We tried to do this in the careers project when we found out that the Html.ActionLink function Html.ActionLink pretty slow when you have several hundred routes defined. There is a problem with signing with the rest of the libraries, which we decided were not worth our time to work now and support future releases of MVC.

+5
source

In the first cshtml template, we can recreate ViewData.TemplateInfo (and remove the PopularObjects list)

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

now we can call another template with the same model

 @Html.DisplayForModel("SecondTemplate") 
0
source

Source: https://habr.com/ru/post/889483/


All Articles