Posts
203
Comments
1119
Trackbacks
51
MVC RadioButtonList HTML Helper

The MVC framework does not come with a built-in RadioButtonList comes standard in ASP.NET web forms.  The MVC Futures assembly (which can be downloaded here) does come with this HtmlHelper but there are some issues with it including the ones described here by Elijah Manor.  He concludes that the method in the futures assembly isn’t very useful and states, “To get around this I ended up writing a foreach creating individual Html.RadioButton and labels.”  However, he did not show the code he wrote to implement it.  I came to a similar conclusion as Elijah so I figured I’d share the code and discuss a couple of interesting issues.

First off, my main goal was to be able to use a Helper that looked something like this:

   1:  <%=Html.RadioButtonList(m => m.Name, new[] { "Foo", "Bar" })%>

to produce HTML that looked something like this:

   1:  <table>
   2:      <tr>
   3:          <td>
   4:              <input id="Name_Foo" name="Name" type="radio" value="Foo" /><label for="Name_Foo" id="Name_Foo_Label">Foo</label>
   5:          </td>
   6:          <td>
   7:              <input id="Name_Bar" name="Name" type="radio" value="Bar" /><label for="Name_Bar" id="Name_Bar_Label">Bar</label>
   8:          </td>
   9:      </tr>
  10:  </table>

This output is very similar to the output produced by the horizontal layout of the web forms RadioButtonList server control – of course if you want to use a non-table approach, have at it! 

Originally, I looked to build a typical HTML helper method internally leveraging the already built HTML RadioButton extension methods that ship with MVC. However, the primary thing I found odd is there does not seem to be a way to create a radio button control where the “id” and “name” attributes are different values (as shown in the above HTML). (Note: if someone knows a way around this, please let me know!)  On the other hand, the controls that come in FluentHtml library of MvcContrib does allow these attributes to correctly have different values.  I *highly* recommend trying out the FluentHtml library as it is a much nicer way (in my opinion) to create views than the standard HTML helpers that ship with MVC. So I built some HTML helpers that leverage the FluentHtml library rather than the built-in library.  The primary difference when authoring the custom HTML helper methods is that the FluentHtml methods extend IViewModelContainer<T> rather than the HtmlHelper class.  This translates to the extension methods hanging off the “this” pointer rather than the Html property of the view page.  My code for the RadioButtonList extensions was:

   1:  public static string RadioButtonList<T>(this IViewModelContainer<T> container, Expression<Func<T, object>> expression, IEnumerable<string> items) where T : class
   2:  {
   3:      var func = expression.Compile();
   4:      var result = func(container.ViewModel);
   5:      var selectList = new SelectList(items, result);
   6:      return container.RadioButtonList(expression, selectList);
   7:  }
   8:   
   9:  public static string RadioButtonList<T>(this IViewModelContainer<T> container, Expression<Func<T, object>> expression, IEnumerable<SelectListItem> items) where T : class
  10:  {
  11:      TagBuilder tableTag = new TagBuilder("table");
  12:      tableTag.AddCssClass("radio-main");
  13:   
  14:      var trTag = new TagBuilder("tr");
  15:      foreach (var item in items)
  16:      {
  17:          var tdTag = new TagBuilder("td");
  18:          var radioTag = container.RadioButton(expression).Value(item.Value ?? item.Text).Checked(item.Selected).LabelAfter(item.Text);
  19:          tdTag.InnerHtml = radioTag.ToString();
  20:   
  21:          trTag.InnerHtml += tdTag.ToString();
  22:      }
  23:      tableTag.InnerHtml = trTag.ToString();
  24:   
  25:      return tableTag.ToString();
  26:  }

Notice on line #18, I am taking advantage of the fluent interface enabled by the FluentHtml library to chain the method calls together. Also, I provide an overload that that you can use either IEnumerable<SelectListItem> or IEnumerable<string>.  Taking advantage of the SelectListItem semantics enables the ability to leverage the “Selected” property of the SelectListItem instance since I’m compiling the expression on line #3 above that was sent in to get the current value of the model’s property. This enabled me to now have this 1 line of code in my view:

   1:  <%=this.RadioButtonList(m => m.Name, new[] { "Foo", "Bar" })%>

If you do not want to use the FluentHtml library, you can still come close to this with the built in extension methods.  The only thing lacking is that the HTML produced will have the same values for the “name” and “id” attributes which is really not technically correct. Also, there’s more manual work to create the “label” tag.  The code using the built-in methods is:

   1:  public static string RadioButtonList2(this HtmlHelper helper, string name, IEnumerable<string> items) 
   2:  {
   3:      var selectList = new SelectList(items);
   4:      return helper.RadioButtonList2(name, selectList);
   5:  }
   6:   
   7:  public static string RadioButtonList2(this HtmlHelper helper, string name, IEnumerable<SelectListItem> items) 
   8:  {
   9:      TagBuilder tableTag = new TagBuilder("table");
  10:      tableTag.AddCssClass("radio-main");
  11:   
  12:      var trTag = new TagBuilder("tr");
  13:      foreach (var item in items)
  14:      {
  15:          var tdTag = new TagBuilder("td");
  16:          var rbValue = item.Value ?? item.Text;
  17:          var rbName = name + "_" + rbValue;
  18:          var radioTag = helper.RadioButton(rbName, rbValue, item.Selected, new { name = name });
  19:          
  20:          var labelTag = new TagBuilder("label");
  21:          labelTag.MergeAttribute("for", rbName);
  22:          labelTag.MergeAttribute("id", rbName + "_label");
  23:          labelTag.InnerHtml = rbValue;
  24:   
  25:          tdTag.InnerHtml = radioTag.ToString() + labelTag.ToString();
  26:   
  27:          trTag.InnerHtml += tdTag.ToString();
  28:      }
  29:      tableTag.InnerHtml = trTag.ToString();
  30:   
  31:      return tableTag.ToString();
  32:  }

The important thing is that this type of pattern can be used to build your own re-usable libraries in your own apps.  Perhaps you don’t want a horizontal layout or you don’t want to use HTML tables. The above approach can be customized to your suit needs.

posted on Wednesday, August 5, 2009 8:09 AM Print
Comments
Gravatar
# re: MVC RadioButtonList HTML Helper
Big Dummy
8/5/2009 8:36 AM
This is probably a stupid question, but what is the type of T in the following?

<%=this.RadioButtonList(m => m.Name, new[] { "Foo", "Bar" })%>

Your method signature is defined as follows (minus the parameters):

public static string RadioButtonList<T>()
Gravatar
# re: MVC RadioButtonList HTML Helper
Steve
8/5/2009 8:40 AM
Not a stupid question at all. The T is used by the IViewModelContainer<T> and is strongly-typed on the view model. so if you were using a Person object as the model you were sending into your view, it allows that to be strongly typed. It's what allows us to write statements like:

<%=Html.RadioButtonList(m => m.Name, new[] { "Foo", "Bar" })%>

with the "m.Name" part being strongly-typed with intellisense rather than relying on "magic strings" to get it right like this:

<%=Html.RadioButtonList("Name", new[] { "Foo", "Bar" })%>
Gravatar
# re: MVC RadioButtonList HTML Helper
Nuri
8/5/2009 12:33 PM
Nice post. I have just posted a radio button list control which uses the same ideas. However, it does not use fluent, but does allow for separation of id and name.
For various reason, as a principal, my HTML control helpers all extend ViewPage (see my other posts on MVC for details).

Thanks!
Gravatar
# re: MVC RadioButtonList HTML Helper
Nuri
8/5/2009 12:38 PM
Sorry, full link to related post is : http://www.plusnconsulting.com/post/MVC-Radio-Button-List.aspx

Gravatar
# re: MVC RadioButtonList HTML Helper
Remus
11/27/2009 1:08 PM
Stupid question - the 2 methods you listed that don't use the FluentHtml, what class did you put those in? Is there a partial class in the MVC framework that can be extended with those 2 methods? I am using MVC 2 beta.

Thank you!
Gravatar
# re: MVC RadioButtonList HTML Helper
Steve
11/27/2009 2:31 PM
@Remus - my methods are "wrapping" the fluent html library. Check out line #18 of the first example above.
Gravatar
# re: MVC RadioButtonList HTML Helper
Joe
7/19/2010 4:37 PM
Thank you for this! It worked out great!
Gravatar
# re: MVC RadioButtonList HTML Helper
Bevan
8/12/2011 5:33 AM
Nice write up - thanks.
Gravatar
# re: MVC RadioButtonList HTML Helper
Ed
9/6/2011 9:51 AM
Great article. But bad to use tables for your layout. :(
Gravatar
# re: MVC RadioButtonList HTML Helper
Steve
9/6/2011 12:23 PM
@Ed - I came to the same conclusion when I wrote this blog post over 2 years ago :) http://geekswithblogs.net/michelotti/archive/2009/08/06/mvc-radiobuttonlist-html-helper-ndash-take-2.aspx
Gravatar
# re: MVC RadioButtonList HTML Helper
Ed
9/7/2011 11:31 AM
Thanks for the link to Part Deux Steve!! Outstanding stuff and I have an immediate need for this helper.

Thanks again.
Gravatar
# re: MVC RadioButtonList HTML Helper
Bhupendar Singh
1/11/2012 8:04 AM
Hi ,
This is the exact article which I am looking for in my project. Thanks for sharing with us. Check this helpful link too...
http://mindstick.com/Articles/ba2249fb-7735-44d4-9736-5a84b5570360/?RadioButtonList%20Control%20in%20ASP.Net

It might be useful for you.
Gravatar
# re: MVC RadioButtonList HTML Helper
Markus
2/16/2013 10:44 PM
@Html.RadioButtonFor(model => Model.RadioButtonValue, true, new { id = "RBValue1" })
@Html.RadioButtonFor(model => Model.RadioButtonValue, false, new { id = "RBValue2" })

This worked fine for me...
Gravatar
# re: MVC RadioButtonList HTML Helper
Ajay
9/3/2013 10:41 AM
Hi,
This is an interesting solution, am a beginner in MVC 4, working on Strongly Typed Database first approach, in Razor pages,
Could you please sent me this implementation cshtml,
I appreciate your help.

Post Comment

Title *
Name *
Email
Comment *  
Verification

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud