Is there a good reason to use the strongly typed html helper...
<%: Html.DisplayTextFor(model => model.Email) %>
As opposed to...
<%: Model.Email %>
Consider the following Model:
public class MyModel
{
public string Name { get; set; }
[DisplayFormat(NullDisplayText = "No value available!")]
public string Email { get; set; }
}
in my view:
<%= Html.DisplayTextFor(m => m.Email) %>
<%: Model.Email %>
The first line will display "No value available" if we leave the Email to be 'null' whereas the second line will not display anything.
Conclusion:
Html.DisplayTextFor will take into consideration the DataAnnotations on your properties, <%: Model.Email %> will not.
Also <%: Model.Email %> will throw an "Object reference error" when the value is null, but <%= Html.DisplayTextFor %> wont.
DisplayTextFor will also get called during "DisplayFor" and "EditFor" execution. This will ensure that any templated helpers will display the text using the correct Templated Helpers if set ... so a change to the single templated helper will propogate through all displays of that text item ... simple display, edit form, create forms etc etc.
Well, DisplayTextFor will not break if you do not pass in a model (or pass null). Model.Email will throw an exception on null. So, DisplayTextFor is more robust.
The point of the strongly typed helpers is to allow greater compile time checking. This is very handy when refactoring.
DisplayTextFor allows using a consistent construct (with the other strongly typed helpers) throughout the page. Some people might find that more appealing.
DisplayTextFor also allows you to pass the names of templates and fields as parameters.
Related
Is there any way to access any attributes (be it data annotation attributes, validation attributes or custom attributes) on ViewModel properties from the view? One of the things I would like to add a little required indicator next to fields whose property has a [Required] attribute.
For example if my ViewModel looked like this:
public class MyViewModel
{
[Required]
public int MyRequiredField { get; set; }
}
I would want to do something in the EditorFor template like so:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<int?>" %>
<div class="label-container">
<%: Html.Label("") %>
<% if (PROPERTY_HAS_REQUIRED_ATTRIBUTE) { %>
<span class="required">*</span>
<% } %>
</div>
<div class="field-container">
<%: Html.TextBox("") %>
<%: Html.ValidationMessage("") %>
</div>
The information you're looking for is in ViewData.ModelMetadata. Brad Wilson's blog post series on Templates should explain it all, especially the post on ModelMetadata.
As far as the other ValidationAttributes go, you can access them via the ModelMetadata.GetValidators() method.
ModelMetadata.IsRequired will tell you if a complex type (or value type wrapped in Nullable<T>) is required by a RequiredAttribute, but it will give you false positives for value types that are not nullable (because they are implicitly required). You can work around this with the following:
bool isReallyRequired = metadata.IsRequired
&& (!metadata.ModelType.IsValueType || metadata.IsNullableValueType);
Note: You need to use !metadata.ModelType.IsValueType instead of model.IsComplexType, because ModelMetadata.IsComplexType returns false for MVC does not consider to be a complex type, which includes strings.
I would suggest not doing that way because you're adding logic in the view which is a bad practice.
Why don't you create a HtmlHelper or LabelExtension, you can call ModelMetaProvider inside the method and find out whether the property has Required attribute decorated?
A Customer has a billing and delivery address, so given the following database schema
Customer(CustomerId)
Address(AddressId)
CustomerAddresses(CustomerId,AddressId)
And the following Enitity Framework class
public class Customer
{
public IEnumerable<Address> Addresses { get; set; }
}
I output my input boxes in my view like so
<% foreach (var address in Model.Addresses) { %>
<%: Html.TextBoxFor(model => address.Address1) %>
<% } %>
When I post the form values after entering DeliveryAddress1 and BillingAddress1 and then iterate over the FormCollection keys I get the following value
Customer.address.Address1 =
"DeliveryAddress1,BillingAddress1"
The question is how do I distinguish between the two records?
I would recommend you using editor templates. This way you don't need to write ugly loops in your views and the helpers will take care of generating proper names for the input fields.
So in your main view instead of writing all the code you've shown simply:
<%: Html.EditorFor(x => x.Addresses) %>
And then create an editor template for an address (~/Views/Home/EditorTemplates/Address.ascx)
<%# Control
Language="C#"
Inherits="System.Web.Mvc.ViewUserControl<YourApp.Models.Address>" %>
<%: Html.TextBoxFor(x => x.Address1) %>
Notice the name and location of the editor template. The location should be in the EditorTemplates folder (it could also be in ~/Views/Shared/EditorTemplates/Address.ascx) and the name should be the same as the name of the class (Address). ASP.NET MVC will take care of rendering the template for each element of the Addresses collection of your model.
Short of overriding ViewData.TemplateInfo.HtmlFieldPrefix with an empty string, is there a way to keep the prefix from coming through on a nested set of strongly-typed EditorFor or DisplayFor helper calls?
Here's the ugly markup/code I have working so far:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.Web.Mvc.SelectList>" %>
<% ViewData.TemplateInfo.HtmlFieldPrefix = ""; %>
<%=Html.DropDownList("sort", Model)%>
I've tried the *For overload that allows specifying htmlFieldName but that only changes the immediate level. If I have a prefix at the point of that call, I just change what is appended to the prefix.
I could write the template markup by hand, but doing so for a SelectList object seems like I will just end up copying over the MVC source with a single tweak since it involves object data binding logic.
If you have a property on your view model, rather than just doing dropdown list directly on the model, you can put a DataAnnontation attribute on it.
public class MyModel
{
[Display(Name="Your Favorite Choices")]
public string[] Choices {get; set;}
}
then in your code
<%= Html.LableFor(m => m.Choices) %><br />
<%=Html.DropDownList("sort", Model.Choices)%>
Should use that name.
[Hope I got it right from memory. :)]
Here's the MSDN link:
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute.displayattribute.aspx
Not sure if I understand your question, but, if you render the "child" views as partials instead of EditorFor then the fields will not be prefixed.
Given the following MVC mark-up:
<p>
<label for="forenamesLabel">Forename(s):</label>
<%: Html.TextBoxFor(model => model.ConsultantRegistrationDTO.Forenames) %>
<%: Html.ValidationMessageFor(model => model.ConsultantRegistrationDTO.Forenames, "*") %>
</p>
How is it possible for me to accurately set the label/#for attribute to the id of the generated TextBox by MVC? If I use #id as an HTML option, wouldn't that break my binding?
You can use
Html.LabelFor(c model => model.ConsultantRegistrationDTO.Forenames)
Then in your model you need to specify the DisplayNameAttribute from System.ComponentModel.DataAnnotations. So your model would look something like this:
public class ConsultantModel
{
[DisplayName("Forenames")]
public string Forenames { get; set; }
}
As was suggested by Keltex you can use Html.LabelFor with a DisplayName decorator. A few considerations that go along with this.
LabelFor and DisplayName decorators are specific to MVC 2. This may or may not be an option for you.
Using this method, specifically using the DisplayName decorator, introduces View concerns into your Model which is sometimes not desirable. It is usually better to create a ViewModel and use the decorator there to preserve separation of concerns.
This also allows you to use the DisplayNameAttribute when your model is auto-generated as you've said yours is.
So let's say I have a view that access a date:
<%= Html.TextBoxFor(model => Model.Birthday) %>
How do I have my view dictate how that date is formatted? I'd like to use common formatting options, and it's important that this is 2-way (both for display and data-entry).
In your model, you could use System.ComponentModel.DataAnnotations metadata to specify the format you want the property in. e.g.
[DisplayFormat(DataFormatString="{0:MM/dd/yyyy}")]
public DateTime Birthday { get; set; }
Then, in your view, use:
<%= Html.EditorFor(model => model.Birthday) %>
Unfortunately, the regular HTML Helpers don't respect the metadata, so you have to use Html.EditorFor.
Use a specific ViewModel class that has a string in the format you want for the birthday and use that to display in the textbox. When you post the form, if you are using default binding for the domain object itself, it should parse the string for you. If you are using a ViewModel, you'll have to parse it upon posting the form.
I was kind of surprised to see that you can't just throw a format string in Html.EditorFor(). I've sinced starting using EditorTemplates.
Create a directory structure in your solution.
Views > Shared > EditorTemplates
Add a new MVC 2 User Control in the EditorTemplates folder and name it DateTime.ascx. Html.EditorFor(DateTime) will now use this user control for display.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>
<%= Html.TextBox(string.Empty, (Model.HasValue ? Model.Value.ToShortDateString() : string.Empty)) %>