Welcome to AspAdvice Sign in | Join | Help

Adding html attributes support for Templates - ASP.Net MVC 2.0 Beta

ASP.Net mvc 2.0 beta, supports templates feature.  Templates are a way to write reusable view code.  They significantly simplifies the code we need to write in views.

You can read more about templates here

In this post I will show a way to extend the ModelMetadata and add support for Html Attributes which can be used in templates.

Let us assume there is a view model for "Employee"

   1: public class Employee
   2: {
   3:     public string Title { get; set; }
   4:     public string FirstName { get; set; }
   5:     public string LastName { get; set; }
   6: }

 Here is the controller’s action to render the view model

   1: public class HomeController : Controller
   2:    {
   3:        public ActionResult Index()
   4:        {
   5:            Employee employee = new Employee();
   6:            return View(employee);
   7:        }
   8:    }

And the view code

<% using(Html.BeginForm())
%>
<p>
<%=Html.EditorFor(e => e.Title)%>
</p>
<p>
<%=Html.EditorFor(e => e.FirstName)%>
</p>
<p>
<%=Html.EditorFor(e => e.LastName)%>
</p>
<%
%>

Html for the Title field will be rendered as the following

<input type="text" value="" name="Title" id="Title"/>

What if we want to limit text box size of the title property and amount of data user can enter in to the textbox? 

Default metadata for the view model won’t allow us to specify these attributes.  Or the EditorFor signature won’t allow us pass in any additional html attributes. 

Instead of EditorFor, we could use TextBoxFor in the simple scenarios.  EditorFor enables the use of templates and it would be nice to have similar support for Html attributes.

One way to solve this is by decorating the view model with our own custom attributes as shown in the following snippet.

public class Employee
{
    [HtmlProperties(Size = 5, MaxLength = 10)]
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Here we decorated the Title property with a custom attribute called “HtmlProperties”.  This attribute has a Size property and MaxLength property.

Code for HtmlPropertiesAttribute is straight forward.

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property,AllowMultiple=false,Inherited=true)]
   public class HtmlPropertiesAttribute : Attribute
   {
       public string CssClass
       {
           get;
           set;
       }
       public int MaxLength
       {
           get;
           set;
       }
       public int Size
       {
           get;
           set;
       }
       public IDictionary<string,object> HtmlAttributes()
       {
           //Todo: we could use TypeDescriptor to get the dictionary of properties and their values
           IDictionary<string, object> htmlatts = new Dictionary<string, object>();
           if (MaxLength != 0)
           {
               htmlatts.Add("MaxLength", MaxLength);
           }
           if (Size != 0)
           {
               htmlatts.Add("Size", Size);
           }
           return htmlatts;
       }
   }

There is an utility method HtmlAttributes that spits out the dictionary of html attributes for use of use later.

 

Next we need to make the metdata provider aware of our new attribute.  MetadaProvides reads the metadata from the properties of view model and makes it available to the templates.

Out of the box implementation for ModelMetaDataProvider is DataAnnotationsModelMetadataProvider.  This provider has overridable method CreateMetadata.

From mvc 2.0 beta, modelmetadata supports a dictionary called AdditionalValues, which we can use as a backing store for our new metadata “HtmlAttributes”

Following is the code for our new metadataprovider

 

public class MetadataProvider : DataAnnotationsModelMetadataProvider
   {
       protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
                                                       Type containerType,Func<object> modelAccessor,Type modelType,string propertyName)
       {
           var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
           var additionalValues = attributes.OfType<HtmlPropertiesAttribute>().FirstOrDefault();
           if(additionalValues != null)
           {
               metadata.AdditionalValues.Add("HtmlAttributes", additionalValues);
           }
           return metadata;
       }
   }

Once the metadata provider is ready, we need to make the template aware of this new property.

Out of the box, mvc provides a string template to render the string properties of the view model.  We can override the default implementation by creating a ViewUserControl called string.ascx  under “Views\Shared\EditorTemplates” folder of mvc project.  EditorTemplates is a new folder we need create to customize the out of box templates.

string.ascx code is shown bellow.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%
   var htmlAttributes = new HtmlPropertiesAttribute();
   if(ViewData.ModelMetadata.AdditionalValues.ContainsKey("HtmlAttributes"))
      htmlAttributes = (HtmlPropertiesAttribute) ViewData.ModelMetadata.AdditionalValues["HtmlAttributes"];
   htmlAttributes.HtmlAttributes().Add("class", "text-box single-line " + htmlAttributes.CssClass);
 %>
    <span>
          <%= Html.Label(ViewData.ModelMetadata.PropertyName) %>
          <%=Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,htmlAttributes.HtmlAttributes()) %>                                      
   </span>

Here we simply retrieve the HtmlAttributes dictionary from the additional values and calls its “HtmlAttributes” method and pass them to TextBox helper method.

We need to one more code change in order to tie every thing together.  In the Global.asax file, application_start method, we need to add code to replace the current ModelMetadataProvider with our implementation of metadataprovider.

protected void Application_Start()
       {
           AreaRegistration.RegisterAllAreas();
 
           RegisterRoutes(RouteTable.Routes);
 
           ModelMetadataProviders.Current = new MetadataProvider();
       }

 Rendered html code for the title after our modifications is as follows

<input type="text" value="" name="Title" id="Title" size="5" maxlength="10"/>

I provided the final solution as an attachment to this post

Published Sunday, November 29, 2009 12:28 PM by kiran chand
Filed under:

Attachment(s): ExtendedEditorFor.zip

Comments

# Adding html attributes support for Templates - ASP.Net MVC 2.0 Beta

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Thursday, January 21, 2010 7:57 AM by DotNetKicks.com

# http://aspadvice.com/blogs/kiran/archive/2009/11/29/Adding-html-attributes-support-for-Templates-_2D00_-ASP.Net-MVC-2.0-Beta_2D00_1.aspx

Tuesday, April 13, 2010 12:49 PM by Twitter Mirror
Anonymous comments are disabled