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:
1: <%=this.RadioSet(m => m.Name).Options(Model.FooBarList) %>
To produce HTML that looks like this:
1: <div id="Name">
2: <input id="Name_Foo" name="Name" type="radio" value="Foo" /><label for="Name_Foo" id="Name_Foo_Label">Foo</label>
3: <input id="Name_Bar" name="Name" type="radio" value="Bar" /><label for="Name_Bar" id="Name_Bar_Label">Bar</label>
4: </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<SelectListItem> and FooBarList is IEnumerable<SelectListItem>. The first thing I tried was creating my list like this:
1: private static IEnumerable<SelectListItem> CreateFooBarList()
2: {
3: var list = new List<string> { "Foo", "Bar" };
4: return new SelectList(list);
5: }
This resulted in HTML that was broken as the id’s and value’s were not set correctly:
1: <div id="Name">
2: <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">Foo</label>
3: <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">Bar</label>
4: </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:
1: private static IEnumerable<SelectListItem> CreateFooBarList()
2: {
3: var list = new List<SelectListItem>
4: {
5: new SelectListItem { Text = "Foo", Value = "Foo" },
6: new SelectListItem { Text = "Bar", Value = "Bar" }
7: };
8: return new SelectList(list);
9: }
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:
1: <div id="Name">
2: <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">System.Web.Mvc.SelectListItem</label>
3: <input id="Name" name="Name" type="radio" value="" /><label for="Name" id="Name_Label">System.Web.Mvc.SelectListItem</label>
4: </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:
1: private static IEnumerable<SelectListItem> CreateFooBarList()
2: {
3: var list = new List<SelectListItem>
4: {
5: new SelectListItem { Text = "Foo", Value = "Foo" },
6: new SelectListItem { Text = "Bar", Value = "Bar" }
7: };
8: return new SelectList(list, "Value", "Text");
9: }
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<SelectListItem> (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.