MVC RadioButtonList HTML Helper – Take 3

In two previous posts, I talked about ways to create your own HTML Helper to generate a radio button list.  In the first post I leveraged the FluentHtml library to create a table layout.  In the second post I switched this to using a div instead of a table.  The crux of the issue was that I wanted to make sure the the "id" and "name" attributes were set correctly and MVC out of the box doesn't seem to do this properly.  Recently I discovered a way to successfully do this without "wrapping" the FluentHtml library but by using it directly while leveraging a "RadioSet" like this:

<%=this.RadioSet(m => m.Name).Options(Model.FooBarList) %>

To produce HTML that looks like this:

<div id="Name">
    <input id="Name_Foo" name="Name" type="radio" value="Foo" /><label for="Name_Foo" id="Name_Foo_Label">Foo</label>
    <input id="Name_Bar" name="Name" type="radio" value="Bar" /><label for="Name_Bar" id="Name_Bar_Label">Bar</label>
</div>

which is exactly what I want.  However, there are a couple of gotchas I ran into for making this work properly.  First off, it's important to note that the Options() method above is taking an IEnumerable and FooBarList is IEnumerable.  The first thing I tried was creating my list like this:

private static IEnumerable<SelectListItem> CreateFooBarList()
{
    var list = new List<string> { "Foo", "Bar" };
    return new SelectList(list);
}

This resulted in HTML that was broken as the id's and value's were not set correctly:

<div id="Name">
    <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">Foo</label>
    <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">Bar</label>
</div>

So realized it was probably because that SelectListItem constructor just didn't assign the Value property when a list of strings was sent in.  So I changed to this:

private static IEnumerable<SelectListItem> CreateFooBarList()
{
    var list = new List<SelectListItem>
    {
        new SelectListItem { Text = "Foo", Value = "Foo" },
        new SelectListItem { Text = "Bar", Value = "Bar" }
    };
    return new SelectList(list);
}

I figured now that I was using an actual strongly-typed SelectList I would be all set.  But unfortunately, the rendered HTML was even worse now:

<div id="Name">
    <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">System.Web.Mvc.SelectListItem</label>
    <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">System.Web.Mvc.SelectListItem</label>
</div>

It turns out that the way I had to fix this was to specify all arguments on the SelectList constructor with the dataTextField and dataValueField as shown on line #8 below:

private static IEnumerable<SelectListItem> CreateFooBarList()
{
    var list = new List<SelectListItem>
    {
        new SelectListItem { Text = "Foo", Value = "Foo" },
        new SelectListItem { Text = "Bar", Value = "Bar" }
    };
    return new SelectList(list, "Value", "Text");
}

Now the HTML and all attributes are correct.  You would think that the constructor of the SelectList would be smart enough to identify that the IEnumerable collection that was sent in was of type IEnumerable (which is what it ultimately wants) and set those arguments for you automatically. But it looks like that was a naive assumption on my part.  Easy enough to add a simple extension method to do this for you throughout your codebase. Make sure to watch out for this in your own code – I won't make this mistake again.

Tweet Post Share Update Email RSS