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.
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 != 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:
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:
@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. :)