Is there a way to choose what DisplayFor renders? - asp.net-mvc

Consider this code:
<%: Html.DisplayFor(model => model.SomeBoolean)%>
<%: Html.DisplayFor(model => model.SomeInt)%>
First one is bound to a boolean this will render a disabled drop down list.
The second is bound to an int and this will simply render the result as text.
I would like the int to also render a drop down list too.
What are my options?
Many thanks.

MVC has a set of default editor and display templates. You can create your own by creating a DisplayTemplates folder in either the view folder for the controller, or you can define a more global template by putting it in a DisplayTemplates folder in the Shared folder.
For system types, use the actual type name and not the language specific shortcut (i.e. for int, call the template Int32.ascx or Int32.cshtml for Razor.
You can further specify templates by either using an overload (I know this is possible for EditorFor, I'm guessing it is for DisplayFor as well), or if you have a specific property that you want to use a different template for, you can use data annotations.
This same technique works for complex types as well.

Related

Custom UIHint attribute

Is it possible to create a custom version of the UIHint attribute?
When my company first adopted MVC, we used a lot of Html.* helper methods. We are in the process of redesigning out MVC template to make use of the full power of MVC. One way we are doing this is with Display and Editor Templates.
However, one popular HTML extension method we had was to generate dropdowns for Enums. One of the options we had was to sort by the int value or the description or text of the EnumMember.
I would like to see about creating a EnumDropdown attribute that accepts several parameters that can customize the output of the HTML dropdown. However, I don't think it's possible to do this while still retaining the benefits of the UIHint attribute. Meaning, that I won't be able to simply call #Html.EditorFor(m => Model)
I had found that there is a System.Web.UI.IAutoFieldGenerator interface but it doesn't appear to do what I want. Any suggestions?
The newer versions of MVC have this built in now:
EnumDropDownListFor HTML Helper
The only thing UIHint does is suggest a Display or Editor template name. MVC will then add this name to the search path when looking for that template.
You can just use UIHint as is and have your generator create these for you in the correct folders and not have to customize it.

How to display the array index number in an editor template

In my model I have a List property of a complex type called EventField.
I created an editor template, which works fine using:
#Html.EditorFor(e => e.EventField)
My question is, within that Editor template I want to pick out the index/sequence number on its own, as a number. This index will simply be added into the template as a data attribute so I can pick it up using javascript.
A couple of answer are out there that don't suffice:
#html.IdFor - outputs the whole name e.g Name[0].other
here - you can't pass through the model name "EventField" this way.
Is there a simple method/function/helper that will give me access to that Index, without having to change how I call the Editor template?
How about:
#using System
#using System.Text.RegularExpressions
var i = Convert.ToInt32(Regex.Matches(
ViewData.TemplateInfo.HtmlFieldPrefix,
#"\[([0-9]+)?\]")[0].Groups[1].ToString());

How can I define the location of a custom editor template when using MVC areas?

It is my understanding that the location is:
~/Views/Shared/EditorTemplates/ViewModelName
However I have many Views folders using areas. Can I define the file to use with some parameter of the call to the
#Html.EditorFor( ...
Those are default lookup paths which RazorViewEngine will search for editor template, in this order:
"~/Areas/{area}/Views/{controller}/EditorTemplates/{templateName}.cshtml",
"~/Areas/{area}/Views/Shared/EditorTemplates/{templateName}.cshtml",
"~/Views/{controller}/EditorTemplates/{templateName}.cshtml",
"~/Views/Shared/EditorTemplates/{templateName}.cshtml",
If not specified, templateName value defaults to object type (in your case 'ViewModelName'). If template with this name is not found by MVC will fall back to resolve rendering using known build-in templates (int, string, collection, object, etc).
You can specify template name to override defaults:
#Html.EditorFor(m => m.MyDate, "_MyTemplate")
You can also specify relative paths:
#Html.EditorFor(m => m.MyDate, "../_MyTemplate")
You cannot specify full paths in any form (ex:"~/Views/Custom/EditorTemplates/ViewModelName") and you should never specify extension in template name (ex: '_MyTemplate.cshtml', or '_MyTemplate.vbhtml')!
You could pass the location of the template as second argument.
#Html.EditorFor(x => x.Foo, "~/Views/Custom/EditorTemplates/ViewModelName.cshtml")
This being said I would avoid doing this and stick to the conventions. This means that if you want to use some editor template from outside the area where it is defined then you probably didn't define this template at the right place and should move it in the Shared folder.

ASP.Net MVC (2) - Getting ModelMetadata from a View without a custom template

How can I (if possible) get to the ModelMetadata from a view without creating a custom template for the given Model's property?
Traditionally, I have just made a new template and altered the template using the ViewData.ModelMetadata.IsRequired (for example). However, I'm looking for how to access this information in the base view and not within a specific editor template.
In terms of why I don't want to go the editor template route - I have a form where I need better control to the markup in terms of attributes on the input elements. EditorTemplates work decent but having a specialized template and ensuring additional ViewData are set to fulfill the attribute values isn't clean in my opinion.
This should give you access:
<%
var metadata = ViewData.ModelMetadata;
%>
You may checkout how the default templates are implemented. It might give you some additional ideas.

ASP.NET MVC 3 - Partial vs Display Template vs Editor Template

So, the title should speak for itself.
To create re-usable components in ASP.NET MVC, we have 3 options (could be others i haven't mentioned):
Partial View:
#Html.Partial(Model.Foo, "SomePartial")
Custom Editor Template:
#Html.EditorFor(model => model.Foo)
Custom Display Template:
#Html.DisplayFor(model => model.Foo)
In terms of the actual View/HTML, all three implementations are identical:
#model WebApplications.Models.FooObject
<!-- Bunch of HTML -->
So, my question is - when/how do you decide which one of the three to use?
What i'm really looking for is a list of questions to ask yourself before creating one, for which the answers can be used to decide on which template to use.
Here's the 2 things i have found better with EditorFor/DisplayFor:
They respect model hierarchies when rendering HTML helpers (e.g if you have a "Bar" object on your "Foo" model, the HTML elements for "Bar" will be rendered with "Foo.Bar.ElementName", whilst a partial will have "ElementName").
More robust, e.g if you had a List<T> of something in your ViewModel, you could use #Html.DisplayFor(model => model.CollectionOfFoo), and MVC is smart enough to see it's a collection and render out the single display for each item (as opposed to a Partial, which would require an explicit for loop).
I've also heard DisplayFor renders a "read-only" template, but i don't understand that - couldn't i throw a form on there?
Can someone tell me some other reasons? Is there a list/article somewhere comparing the three?
EditorFor vs DisplayFor is simple. The semantics of the methods is to generate edit/insert and display/read only views (respectively). Use DisplayFor when displaying data (i.e. when you generate divs and spans that contain the model values). Use EditorFor when editing/inserting data (i.e. when you generate input tags inside a form).
The above methods are model-centric. This means that they will take the model metadata into account (for example you could annotate your model class with [UIHintAttribute] or [DisplayAttribute] and this would influence which template gets chosen to generate the UI for the model. They are also usually used for data models (i.e. models that represent rows in a database, etc)
On the other hand Partial is view-centric in that you are mostly concerned with choosing the correct partial view. The view doesn't necessarily need a model to function correctly. It can just have a common set of markup that gets reused throughout the site. Of course often times you want to affect the behavior of this partial in which case you might want to pass in an appropriate view model.
You did not ask about #Html.Action which also deserves a mention here. You could think of it as a more powerful version of Partial in that it executes a controller child action and then renders a view (which is usually a partial view). This is important because the child action can execute additional business logic that does not belong in a partial view. For example it could represent a shopping cart component. The reason to use it is to avoid performing the shopping cart-related work in every controller in your application.
Ultimately the choice depends on what is it that you are modelling in your application. Also remember that you can mix and match. For example you could have a partial view that calls the EditorFor helper. It really depends on what your application is and how to factor it to encourage maximum code reuse while avoiding repetition.
You certainly could customize DisplayFor to display an editable form. But the convention is for DisplayFor to be readonly and EditorFor to be for editing. Sticking with the convention will ensure that no matter what you pass into DisplayFor, it will do the same type of thing.
Just to give my 2c worth, our project is using a partial view with several jQuery tabs, and each tab rendering its fields with its own partial view. This worked fine until we added a feature whereby some of the tabs shared some common fields. Our first approach to this was to create another partial view with these common fields, but this got very clunky when using EditorFor and DropDownListFor to render fields and drop downs. In order to get the ids and names unique we had to render the fields with a prefix depending on the parent partial view that was rendering it:
<div id="div-#(idPrefix)2" class="toHide-#(idPrefix)" style="display:none">
<fieldset>
<label for="#(idPrefix).Frequency">Frequency<span style="color: #660000;"> *</span></label>
<input name="#(idPrefix).Frequency"
id="#(idPrefix)_Frequency"
style="width: 50%;"
type="text"
value="#(defaultTimePoint.Frequency)"
data-bind="value: viewState.#(viewStatePrefix).RecurringTimepoints.Frequency"
data-val="true"
data-val-required="The Frequency field is required."
data-val-number="The field Frequency must be a number."
data-val-range-min="1"
data-val-range-max="24"
data-val-range="The field Frequency must be between 1 and 24."
data-val-ignore="true"/>
#Html.ValidationMessage(idPrefix + ".Frequency")
... etc
</fieldset>
</div>
This got pretty ugly so we decided to use Editor Templates instead, which worked out much cleaner. We added a new View Model with the common fields, added a matching Editor Template, and rendered the fields using the Editor Template from different parent views. The Editor Template correctly renders the ids and names.
So in short, a compelling reason for us to use Editor Templates was the need to render some common fields in multiple tabs. Partial views aren't designed for this but Editor Templates handle the scenario perfectly.
Use _partial view approach if:
View Centric Logic
What to keep all _partial view related HTML in this view only. In the template method, you will have to keep some HTML outside the Template View like "Main Header or any outer border/settings.
Want to render partial view with logic (From controller) using URL.Action("action","controller").
Reasons to use Template:
Want to remove ForEach(Iterator). Template is well enough to identify Model as a list type. It will do it automatically.
Model Centric Logic. If multiple views are found in the same displayfor Template folder, then rendering will depend on Passed Model.
Another difference that hasn't been mentioned so far is that a partialview doesn't add model prefixes while a template does
Here is the issue

Resources