Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Html.RatingFor: Extending the MVC HtmlHelper

4.80/5 (8 votes)
16 Dec 2013CPOL1 min read 18.5K  
Html.RatingFor: Extending the MVC HtmlHelper

Introduction

When working on a web application, I was in need to add a rating for a product. That rating will be between 1 and 5 and will always be an int. So my model has a property like public int Rating {get;set;}. I decided to add 5 radio buttons, and each will hold the corresponding rating value.

But then (as always happens), the requirement changed. We didn't want to have only 1 rating property, but 5. So adding 5 radios for each was something that I didn't want to happen.

In order to solve this problem, I created an extension method for the HtmlHelper class that we normally use in our MVC applications. As you may notice, in the method I created all the logic for adding the set of radio buttons needed for the rating process.

C#
public static MvcHtmlString RatingFor<tmodel,>(
    this HtmlHelper<tmodel> htmlHelper, Expression<func<tmodel,>>
    expression, int from, int to, object htmlAttributes = null)
{
    var builder = new StringBuilder();

    var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);

    var model = metadata.Model;
            var name = ExpressionHelper.GetExpressionText(expression);

    var attributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);

    var fullName = 
    htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);

    int direction = 1;
    if (from > to)
        direction = -1;

    for (var i = from; direction == 1 ? i <= to : i >= to; i += direction)
    {
        var tagBuilder = new TagBuilder("input");
        tagBuilder.MergeAttributes(attributes);
        tagBuilder.MergeAttribute("type", "radio");
        tagBuilder.MergeAttribute("name", fullName, true);
        tagBuilder.MergeAttribute("value", 
        i.ToString(CultureInfo.InvariantCulture));
        //If model has a value we need to select it
        if (model != null && model.Equals(i))
        {
            tagBuilder.MergeAttribute("checked", "checked");
        }
        tagBuilder.GenerateId(fullName);

        ModelState modelState;
        if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
        {
            if (modelState.Errors.Count > 0)
            {
                tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
            }
        }
        tagBuilder.MergeAttributes(htmlHelper.
        GetUnobtrusiveValidationAttributes(name, metadata));

        builder.AppendLine(tagBuilder.ToString(TagRenderMode.SelfClosing));
    }

    return MvcHtmlString.Create(builder.ToString());
}

One important part of this code is:

C#
if (model != null && model.Equals(i))
{
 tagBuilder.MergeAttribute("checked", "checked");
}

where we assign the value of the property if it is already set. This is useful when you use this method on an Edit process.

Now on your view, instead of having to create all that radio buttons manually, you can have something like this:

C#
@Html.RatingFor(model => model.Rating, 1, 5)

in order to add a rating from 1 to 5.

Hopefully, you will find this useful. If you have created another useful helper, it would be nice if you share it with the community. :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)