Search code examples
c#razorhtml-helperasp.net-mvc-4

Accessing attributes from Custom Html Helpers in asp.net mvc 4 razor


I have created HtmlHelper in ASP.NET MVC 4 razor view engine C#.
Can I pass view model property to my helper class?
For example, I have property

[Required]
[Display(Name = "Your Lastname")]
public string Lastname { get; set; }

Can I pass this property to my helper something like this @Html.Example(model => model.Lastname) and then get data annotations in helper (if this field is required what is display name and etc.)?


Solution

  • The [Display] attribute enriches the metadata. So you could fetch the information from the metadata.

    For example if you wanted to retrieve the display name inside the helper:

    public static class HtmlHelpers
    {
        public static IHtmlString Example<TModel, TProperty>(
            this HtmlHelper<TModel> html, 
            Expression<Func<TModel, TProperty>> ex
        )
        {
            var metadata = ModelMetadata.FromLambdaExpression(ex, html.ViewData);
            var displayName = metadata.DisplayName;
            return new HtmlString(html.Encode(displayName));
        }
    }
    

    and then if we assume that you have a view model:

    public class MyViewModel
    {
        [Required]
        [Display(Name = "Your Lastname")]
        public string Lastname { get; set; }
    }
    

    you could use the helper in your strongly typed view:

    @model MyViewModel
    
    @Html.Example(x => x.Lastname)
    

    Now let's suppose that you wrote a custom metadata attribute:

    public class FooBarAttribute : Attribute, IMetadataAware
    {
        public FooBarAttribute(string bar)
        {
            Bar = bar;
        }
        public string Bar { get; private set; }
    
        public void OnMetadataCreated(ModelMetadata metadata)
        {
            metadata.AdditionalValues["foo"] = Bar;
        }
    }
    

    that you used to decorate your model with:

    public class MyViewModel
    {
        [Required]
        [FooBar("This is the bar")]
        public string SomeBar { get; set; }
    }
    

    and then inside your helper you could fetch the custom attribute:

    public static class HtmlHelpers
    {
        public static IHtmlString Example<TModel, TProperty>(
            this HtmlHelper<TModel> html, 
            Expression<Func<TModel, TProperty>> ex
        )
        {
            var metadata = ModelMetadata.FromLambdaExpression(ex, html.ViewData);
            if (metadata.AdditionalValues.ContainsKey("foo"))
            {
                var foo = metadata.AdditionalValues["foo"] as string;
                return new HtmlString(html.Encode(foo));
            }
            return MvcHtmlString.Empty;
        }
    }
    

    UPDATE:

    It seems that you need to fetch the Required message. No idea why you need to do this in a custom helper but here's an example how you could achieve that:

    public static class HtmlHelpers
    {
        public static IHtmlString Example<TModel, TProperty>(
            this HtmlHelper<TModel> html, 
            Expression<Func<TModel, TProperty>> ex
        )
        {
            var me = (ex.Body as MemberExpression);
            if (me != null)
            {
                var required = me
                    .Member
                    .GetCustomAttributes(typeof(RequiredAttribute), false)
                    .Cast<RequiredAttribute>()
                    .FirstOrDefault();
                if (required != null)
                {
                    var msg = required.FormatErrorMessage(me.Member.Name);
                    return new HtmlString(html.Encode(msg));
                }
            }
            return MvcHtmlString.Empty;
        }
    }