ASP.NET MVC Html.RadioButtonList Blues
1 min read

Recently I upgraded our ASP.NET MVC project from Preview 5 to RC2. At first I thought the Html.RadioButtonList extension was removed completely, but then realized that it was no longer in the main MVC assembly, but was moved to the Futures project (although I don't know why).

The Preview 5 version of the Html.RadioButtonList rendered the following output...

However, once I got my code to compile I went to run my application only to find that it rendered two RadioButtons with no labels! Where did the labels go?

I pulled down the source code for the 1.0 release (just to make sure it wasn't fixed in the RTM as opposed to the RC2) and dove into the extension code. Nowhere did I see the labels being applied in the extension.

//C:\...\MVC-RTM\MVC\src\MvcFutures\Mvc\RadioExtensions.cs
private static string[] RadioButtonListInternal(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, bool usedViewData, IDictionary<string, object> htmlAttributes) {
   if (String.IsNullOrEmpty(name)) {
      throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
   }
   if (selectList == null) {
      throw new ArgumentNullException("selectList");
   }

   // If we haven't already used ViewData to get the entire list of items then we need to
   // use the ViewData-supplied value before using the parameter-supplied value.
   if (!usedViewData) {
      object defaultValue = htmlHelper.ViewData.Eval(name);

      if (defaultValue != null) {
         IEnumerable defaultValues = new[] { defaultValue };
         IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture);
         HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
         List<SelectListItem> newSelectList = new List<SelectListItem>();

         foreach (SelectListItem item in selectList) {
            item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
            newSelectList.Add(item);
         }

         selectList = newSelectList;
      }
   }

   IEnumerable<string> radioButtons = selectList.Select<SelectListItem, string>(item => htmlHelper.RadioButton(name, item.Value, item.Selected, htmlAttributes));

   return radioButtons.ToArray();
}

Next I looked at the Unit Tests for the extesion. I was please to see tests for the RadioButtonList, but was shocked to see that the asserts were verifying html that didn't have any labels applied!

//C:\...\MVC-RTM\MVC\test\MvcFuturesTest\Mvc\Test\RadioExtensionsTest.cs
[TestMethod]
public void RadioButtonListItemSelected() {
   // Arrange
   HtmlHelper htmlHelper = TestHelper.GetHtmlHelper(new ViewDataDictionary());

   // Act
   string[] html = htmlHelper.RadioButtonList("FooList", GetRadioButtonListData(true));

   // Assert
   Assert.AreEqual(@"<input id=""FooList"" name=""FooList"" type=""radio"" value=""foo"" />", html[0]);
   Assert.AreEqual(@"<input id=""FooList"" name=""FooList"" type=""radio"" value=""bar"" />", html[1]);
   Assert.AreEqual(@"<input checked=""checked"" id=""FooList"" name=""FooList"" type=""radio"" value=""baz"" />", html[2]);
}

private static SelectList GetRadioButtonListData(bool selectBaz) {
   List<RadioItem> list = new List<RadioItem>();
   list.Add(new RadioItem { Text = "text-foo", Value = "foo" });
   list.Add(new RadioItem { Text = "text-bar", Value = "bar" });
   list.Add(new RadioItem { Text = "text-baz", Value = "baz" });
   return new SelectList(list, "value", "TEXT", selectBaz ? "baz" : "something-else");
}

private class RadioItem {
   public string Text {
      get;
      set;
   }

   public string Value {
      get;
      set;
   }
}

Where does that leave us? I guess using the Futures Html.RadioButtonList isn't all that helpful after all. So, instead I decided to loop through my DataSource and create individual Html.RadioButton and Label combinations.

<%
var radioButtonList = new SelectList(new List<ListItem> {
   new ListItem { Text = "Current", Value="false", Selected=true },
   new ListItem { Text = "Other", Value="true"}}, "Value", "Text", "false");
var htmlAttributes = new Dictionary<string, object> {
   { "class", "radioButtonList" },
   { "onclick", "if(eval(this.value)) { $('#tblDate').show('slow'); } else { $('#tblDate').hide('slow'); }" }
};
foreach (var radiobutton in radioButtonList) { %>
   <%=Html.RadioButton("rblDate", radiobutton.Value, radiobutton.Selected, htmlAttributes)%>
   <label><%=radiobutton.Text%></label>
<% } %>

Edit post on GitHub


JavaScript Unit Testing Frameworks
CodeStock 2009 Call for Speakers