Search code examples
asp.net-mvcdata-annotationsmodelmetadata

Custom Html helper that can browse DataAnnotations


Say I have a model like this

public class User
{
    [Required]
    [StringLength(14, ErrorMessage = "Can only be 14 characters long")]
    public string UserName;

}

I want to create a Html helper like this:

@Html.ValidatableEditorFor(m => m.UserName)

so that it spits out a text field with the correct format for jQuery Vaidation plugin to be able to validate, like this:

   <input type="text" class="required" maxlength="14" />

From my research, it seems that there is no way to iterate over all the data annotations in a MetaDataModel so that I can check to see which one's are applicable to jQuery Validation.

How I envision it working in pseudo code:

    var tag = new TagBuilder("input");
    tag.mergeAttribute("type", "text");
    foreach(var attribute in metadata.attributes)
    {
       CheckForValidatableAttribute(attribute, tag);
    }

...
    private void CheckForValidatableAttribute(DataAnnotation attribute, TagBuilder tag)
    {
        switch(attribute.type)
       {
          case Required:
             tag.addClass("required");
             break;
          case StringLength
             tag.mergeAttribute("maxlength", attribute.value)
             break;
       }
    }

How could I go about achieving a helper like this? I want it to work on data annotations so that I don't have to duplicate the validation literals.

For instance, the current Html helpers like TextEditorFor do append validatable attributes to their output fields. How does it do this, and how can I make my own implementation?

Cheers


Solution

  • You may use this simple condition:

    if(attribute.Type is ValidationAttribute)
    {
       string className = attribute.Type.Name.Replace("Attribute", "").ToLower();
    }
    

    UPDATE

    Define an Html helper:

    public static MvcHtmlString ValidationEditorFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, 
            Expression<Func<TModel, TProperty>> expression)
    {
        ....
    }
    

    Create this helper method:

    private static string GetPropertyNameFromExpression<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        MemberExpression memberExpression = expression.Body as MemberExpression;
        if (memberExpression == null)
            throw new InvalidOperationException("Not a memberExpression");
    
        if (!(memberExpression.Member is PropertyInfo))
            throw new InvalidOperationException("Not a property");
    
        return memberExpression.Member.Name;
    }
    

    Now use this in ValidationEditorFor:

    var propertyName = GetPropertyNameFromExpression(htmlHelper, expression);
    var propertyType = typeof(TModel).GetProperties().Where(x=>x.Name == propertyName).First().PropertyType;
    var attributes = propertyType.GetCustomAttributes(true).OfType<ValidationAttribute>();
    

    Now you can check the attributes.... rest is easy.