I understand that in Razor, #Html does a bunch of neat things, like generate HTML for links, inputs, etc.
But I don't get the DisplayFor function...
Why would I write:
#Html.DisplayFor(model => model.Title)
when I could just write:
#Model.Title
Html.DisplayFor() will render the DisplayTemplate that matches the property's type.
If it can't find any, I suppose it invokes .ToString().
If you don't know about display templates, they're partial views that can be put in a DisplayTemplates folder inside the view folder associated to a controller.
Example:
If you create a view named String.cshtml inside the DisplayTemplates folder of your views folder (e.g Home, or Shared) with the following code:
#model string
#if (string.IsNullOrEmpty(Model)) {
<strong>Null string</strong>
}
else {
#Model
}
Then #Html.DisplayFor(model => model.Title) (assuming that Title is a string) will use the template and display <strong>Null string</strong> if the string is null, or empty.
I think the main benefit would be when you define your own Display Templates, or use Data annotations.
So for example if your title was a date, you could define
[DisplayFormat(DataFormatString = "{0:d}")]
and then on every page it would display the value in a consistent manner. Otherwise you may have to customise the display on multiple pages. So it does not help much for plain strings, but it does help for currencies, dates, emails, urls, etc.
For example instead of an email address being a plain string it could show up as a link:
#ViewData.TemplateInfo.FormattedModelValue
DisplayFor is also useful for templating. You could write a template for your Model, and do something like this:
#Html.DisplayFor(m => m)
Similar to #Html.EditorFor(m => m). It's useful for the DRY principal so that you don't have to write the same display logic over and over for the same Model.
Take a look at this blog on MVC2 templates. It's still very applicable to MVC3:
http://www.dalsoft.co.uk/blog/index.php/2010/04/26/mvc-2-templates/
It's also useful if your Model has a Data annotation. For instance, if the property on the model is decorated with the EmailAddress data annotation, DisplayFor will render it as a mailto: link.
In general if it is used for just one property it appears that the generated HTML is the same.
For example this code
<td>#Html.DisplayFor(modelItem=>item.Genre.Name)</td>
<td>#item.Genre.Name, This is direct from Item</td>
generates this HTML
<td>myClassNameProperty</td>
<td>myClassNameProperty, This is direct from Item</td>
At the same time now if i want to display all properties in one statement for my class "Genre" in this case,
i can use #Html.DisplayFor() to save on my typing, for least
i can write #Html.DisplayFor(modelItem=>item.Genre) in place of writing a separate statement for each property of Genre as below
#item.Genre.Name
#item.Genre.Id
#item.Genre.Description
and so on depending on number of properties.
3rd party edit
From html-helpers documentation:
An HTML Helper is just a method that returns a string. The string can
represent any type of content that you want. For example, you can use
HTML Helpers to render standard HTML tags like HTML and
tags. You also can use HTML Helpers to render more complex content
such as a tab strip or an HTML table of database data.
To render more complex html mvc custom html templates or custom tag helpers in asp.net core are used.
Would like to elaborate with a scenario on the reason to use DisplayFor rather than simply using the model value.
The application/significance of what others have mentioned that "DisplayFor uses display template to render", is for example for a boolean, this displays a checkbox
#Html.DisplayFor(model => model.IsBooleanTrue)
Sidenote: EditorFor would display the same checkbox that is not disabled
Documentation: https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.html.displayextensions.displayfor?view=aspnet-mvc-5.2 (no examples in there unfortunately)
The main difference between #Html.DisplayFor(model => model.Title) and #Model.Title
Html.DisplayFor() is a strongly typed method that can be used to display the value of a model property in a view. It is used to display the value of the property in a way that is appropriate for the data type of the property. It will also respect any DataAnnotations attributes applied to the property and display the property accordingly.
#Model.Title directly outputs the value of the "Title" property to the view. It does not use any formatting or data annotation attribute to display the value.
In summary,
#Html.DisplayFor(model => model.Title) is a more robust and safe way to display a property, since it takes care of formatting and validation, while #Model.Title is a simple and quick way to output a property value.
Related
I googled the problem too many times. But I couldnt find any solution to do this.
I want to create a custom DataType with a default EditorTemplate and DisplayTemplate for use in mvc3 razor.
Model
[DataType("MyCustomDataType")]
public MyType Property { get; set; }
// I mean by MyType any type of data: string, int, datetime and so on
View - Razor
#Html.DisplayFor(m => m.Property)
//or
#Html.EditorFor(m => m.Property)
Actually I want to create a reuseable datatype with default editor-template and display-template.
Do you have any suggestion please? Can you give me a useful link or if you can, a simple example please? Thanks in advance.
this is an excellent tutorial for custom editor/display template. Although its not in razor syntax, its easily convertible.
If you want to create editor template depending on Datatype attribute, you can get the attribute value using ViewData.ModelMetadata.AdditionalValues["DataTypeAttribute"]
If the value is your custom datatype value, do what ever you want. Otherwise, do the default.
Not sure if it is a good idea. But it should work.
You can create a new type, i.e. a Person class that you can include in the model that you send to the view. i.e. Model.Person.
By defining a new EditorTemplate and DisplayTemplate with the name Person. By default, calling #Html.EditorTemplateFor(x=>x.Person); will look for a EditorTemplate for the type.
Have a look at the following stackoverflow post on "How to use asp.net mvc Editor Templates"
So normally I am doing something like this:
#Html.EditorFor(m => m.MyDateTime)
Then I have a custom template DateTime.cshtml that is used as the editor.
Whatever the date value of Model.MyDateTime is will be displayed as expected, and as expected the name of the field on the next POST will be MyDateTime.
My desire is to use the custom template in the Html.EditorFor WITHOUT binding in the model object, instead I wish to give it a form field name to be POSTed but have it start out blank.
However I can't find an override of Html.EditorFor() that will allow me to not specify a model object, so I can only specify the template to use and the html form field name so it starts empty.
Note: I tried #Html.EditorForModel("DateTime", "MyDateTime") but just got an error so I think that I misunderstood what that is for.
(I know I could just have MyDateTime be null coming back from the controller but that is not what I am asking here.)
Why would you want to use an EditFor that is going to edit nothing (no model passed)? Instead of going down that road, you should probably look at using a View or PartialView which do not require having a Strongly-Typed model.
I am working on a globalized web-app in ASP.NET MVC3. The project contains the I18N resources files and I normally access the resources inside my Razor views like...
#I18N.MyResourceString
I have a tricky situation which I have not been able to figure out a elegant solution to yet. I need to be able to localized the sentence "Click here to donate." where the word 'here' should be a link to our donation system.
Most links in the site are internal so to create link I simply write...
#Html.ActionLink("Some link text", "MyAction", "MyController")
This donation link is external. What I have so far (which is not working) is...
#String.Format(I18N.ClickHereToDonate, "" + I18N.Here + "")
where the I18N.ClickHereToDonate resource's text is "Click {0} to donate.".
What I see on the screen is...
Click here to donate.
Furthermore, I would also like to add a 'title' attribute to the 'a' tag. It gets even uglier when I try that...
#String.Format(I18N.ClickHereToDonate, "" + I18N.Here + "")
There has to be a better way to form complex strings with embedded tags without concatenating things together in such a hackish manner. Not only does it not work (the intended markup got encoded) but it makes the HTML inside a string literal in my razor template which makes me loose any awesome IDE support/intergation/refactoring capabilities.
How can markup be injected into localized strings?
UPDATE
Adam Tuliper mentioned the #Html.Raw helper method in his answer so I added it to my already ugly markup...
#Html.Raw(String.Format(I18N.ClickHereToDonate, "" + I18N.Here + ""))
This at least got me a click-able link in the outputted markup.
Click here to donate.
It is still a far-less-than-elegant solution though so I am still looking for better ways of doing this.
Maybe try
#I18N.ClickHereToDonate
Overall, you don't need the String format - you can just inject the Razor things inside normal html elements.
Edit: Incorporating the below:
#Html.Raw(String.Format(#I18N.ClickHereToDonate,String.Format("<a href='http://paypal.com' title='{0}'>{1}</a>", I18N.PayPal,I18N.Here)))
Your choices are limited without built in support for this scenario (and there isn't in the helpers)
The cleaner way is to form your urls in a viewmodel and pass that view model to the view so you have minimal html. Your ViewModel contains
public class WhateverIndexViewModel
{
public string Key {get;set;}
public string URI {get;set;}
public string Title {get;set;}
}
Set the info in your controller, pass it to your view and use
#Links["YourKey"].Title"
As a basic idea.
Note if you dont want to use Html.Raw here then your URI in the class would be of type MvcString not String this way #Links["YourKey"].URI won't be html encoded.
I've created custom Editor templates and Display templates. I've placed each of these types in a folder within my views folder. The folders were named either EditorTemplate or DisplayTemplate, depending upon which type of template was created.
So, now I can use EditorFor to use my custom editor template, or DisplayFor for my custom editor template.
I would like to create a custom template for a LabelFor, but I haven't found an example of this. Would I create a folder named Labeltemplate in my Views folder and add it here?
UPDATE
The reason I was trying to extend the LabelFor was to handle a Property that is of type KeyValuePair. I want to use the Key of this property as the Label, and the value as the Display. I asked a question here about the DisplayFor to handle the Value.
My solution ended up as>
#Html.DisplayFor(m => m.MyProperty, #Model.MyProperty.Key)
Thanks,
LabelFor doesn't use any templates. It is hardcoded in the MVC source code and it spits a <label> no matter what you do.
You will have to write a custom html helper if you want to modify this behavior.
On the other hand if you want to use templates you have to use EditorFor/DisplayFor helpers. So, since a label is for displaying purposes you could use a display template and instead of using Html.LabelFor(x => x.Foo) use Html.DisplayFor(x => x.Foo). As far as the custom template is concerned, either you decorate the Foo property with the [UIHint] attribute or pass it as second argument to the DisplayFor helper.
UPDATE:
According to your comment you are not trying to modify the markup but only the value. That's what the second argument of the LabelFor helper could be used for:
#Html.LabelFor(x => x.Foo, Model.Key)
#Html.EditorFor(x => x.Foo)
This creates a label which is associated with the Foo input (for attribute of the label properly assigned) but the text shown is that of the Key property on your view model.
There is no support for creating a template for a specific HTML Helper method (LabelFor).
You could:
Markup your model using meta descriptors to change what value gets displayed as part of the label:
[DisplayName("Custom Label")]
public string Test {get;set;}
You could create your own custom HTML Helper method for rending out a label:
How can I override the #Html.LabelFor template?
You can create a DisplayTemplate and access it via template name:
#Html.DisplayFor(x => x.Foo, "label")
And then just create a template called label.cshtml in your DisplayTemplates folder.
To simplify this call, you can write an extension method that handles this call:
public static MvcHtmlString TemplateLabelFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> property)
{
return html.DisplayFor(property, "label");
}
I have what appears (to me anyway) to be a strange problem...
I created a simple editor template for a SelectListItem (SelectListItem.cshtml in the ~/Views/Shared/EditorTemplates folder), for example:
<ul class="select-list-item cell-15 col-3 omega clearfix">
#Html.EditorFor(c => c.Categories)
</ul>
Where c.Categories is an IEnumerable
This worked fine, but I wanted another template to render the collection with slightly different markup, so I copied and renamed the editor template to, for example, 'CategoryIcons.cshtm' and invoked as follows:
<ul class="select-list-item cell-15 col-3 omega clearfix">
#Html.EditorFor(c => c.Categories, "CategoryIcons")
</ul>
In short, the only difference is I'm specifying a named editor template.
When I open the page, I now get the following error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[System.Web.Mvc.SelectListItem]', but this dictionary requires a model item of type 'System.Web.Mvc.SelectListItem'
The template's model declaration, in both templates id:
#model System.Web.Mvc.SelectListItem
I don't understand why the default template works and the named template doesn't. Any help would be appreciated.
Thanks.
When you call #Html.EditorFor(c => c.Categories) it is falling back to the default template for IEnumerable. This default template is provided by the MVC framework, and its behaviour is to output Html.EditorFor() for each item in the enumeration. That in turn emits the appropriate editor template for each item in the list individually - in your case they're all instances of SelectListItem, so in the first case, the SelectListItem template is used for each item.
In the second case, by explicitly setting your EditorFor to use a particular editor template CategoryIcons, you are telling it to use that editor template for the whole enumeration, instead of allowing the enumerable to be templated by default, in turn using the template for each enumerated item.
I'm not sure of the best way around this just yet.
One approach would be to define a CategoryIcons template, whose model is an instance of IEnumerable<CategoryIcon>, which simply foreaches the Model enumeration, and performs Html.EditorFor for each item, with an explicit template reference of CategoryIcon. You then put your per-item editor template in that template (CategoryIcon not CategoryIcons). You would then call this by doing #Html.EditorFor(c => c.Categories, "CategoryIcons").
I'm going to have a look around to see if there are better ways to get this done, but I hope this might be useful for now. It would be great if templates could be parameterized, so you could write an IEnumerable template that takes as an argument the name of the template to use for each of its items.
Just an update, I stumbled on this question trying to address the same issue myself.
What I ended up doing was iterating through each instance of the collection and calling the EdtorFor individually, sort of like this:
<ul class="select-list-item cell-15 col-3 omega clearfix">
#for (int i=0;i<Model.Categories.Count;i++) {
#Html.EditorFor(c => c.Categories[i], "CategoryIcons")
}
</ul>
Still not clean, but I like it better than inheriting a new class like you ended up doing.
(Sorry if my C# syntax is a little off, I write in vb.net)
If you're using EditorFor, I don't think the looping solution will work. It seems that using the IEnumerable template is the only way for the form inputs to be named correctly; if you just call EditorFor repeatedly, then your form <INPUT>s will have the same ID, rather than indexed IDs.
I experienced this issue and the solution was to emit the enumerable template and not loop through the list items.
Sorry to make this an answer not a comment -- don't have commenting rights.