Pages

Saturday 27 February 2010

ASP.NET MVC Html.RadioButton Generates Invalid XHTML

It was pointed out to me by a colleague that the Html.RadioButton(...) extension methods produce invalid XHTML. The output is invalid because, by default, the method will duplicate html "id" attributes. For example, let's say I have a Colours View Model...

public enum Colours
{
    Red,
    Blue,
    Green
}

public class ColoursViewModel
{
    public Colours ChosenColour
    {
        get;
        set;
    }
}

Then I could create a simple action like this ...

public class TestController
{
    public ActionResult SelectColour()
    {
        return View(new ColoursViewModel());
    }
}

... that rendered the following view ...

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ColoursViewModel>" %>

<%= Html.RadioButton("ChosenColour", "Red"); %>
<%= Html.RadioButton("ChosenColour", "Green"); %>
<%= Html.RadioButton("ChosenColour", "Blue"); %>

The MVC framework will automatically bind my view model's "ChosenColour" property to the radio button collection so that I can post the results back like this...

public class TestController
{
    [AcceptVerbs(HttpVerbs.Post)]
    public string SelectColour(ColoursViewModel viewModel)
    {
        return "You chose " + viewModel.ChosenColour.ToString();
    }
}

The problem here is that the generated html looks like this (notice the duplicate id attributes)...





This means that we cannot use the "for" attribute on any labels associated with the individual radio buttons nor can we use javascript to select the radio button when that associated label is clicked.

This is how you fix the problem...

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ColoursViewModel>" %>

<%= Html.RadioButton("ChosenColour", "Red", new { id = "chooseRed" }); %>
<%= Html.RadioButton("ChosenColour", "Green", new { id = "chooseGreen" }); %>
<%= Html.RadioButton("ChosenColour", "Blue", new { id = "chooseBlue" }); %>

... which will generate this html fragment...





The point I'm trying to make here is that the id attribute should be a first class parameter of the RadioButton extension method. I shouldn't have to override it using the htmlAttributes parameter. This is what the official extension methods look like...

public static string RadioButton(this HtmlHelper htmlHelper, string name, object value);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, object htmlAttributes);
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, IDictionary<string, object> htmlAttributes);

You could consider making a change to the MVC framework libraries to include the id, thus decoupling it from the name. For example..

public static string RadioButton(this HtmlHelper htmlHelper, string name, string id, object value);

So anyway, the take-away from this blog post is "Html.RadioButton(...) will produce invalid XHTML unless you pay attention."

2 comments:

  1. Thank you for this.

    The syntax for VB is:
    <%= Html.RadioButton("ChosenColour", "Blue", New With {.id = "choosered"}); %>

    ReplyDelete
  2. My pleasure. Thanks for the comment.

    ReplyDelete