Posts
202
Comments
1112
Trackbacks
51
MVC 2 Editor Template for Radio Buttons

A while back I blogged about how to create an HTML Helper to produce a radio button list.  In that post, my HTML helper was “wrapping” the FluentHtml library from MvcContrib to produce the following html output (given an IEnumerable list containing the items “Foo” and “Bar”):

   1:  <div>
   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>

With the release of MVC 2, we now have editor templates we can use that rely on metadata to allow us to customize our views appropriately.  For example, for the radio buttons above, we want the “id” attribute to be differentiated and unique and we want the “name” attribute to be the same across radio buttons so the buttons will be grouped together and so model binding will work appropriately. We also want the “for” attribute in the <label> element being set to correctly point to the id of the corresponding radio button.  The default behavior of the RadioButtonFor() method that comes OOTB with MVC produces the same value for the “id” and “name” attributes so this isn’t exactly what I want out the the box if I’m trying to produce the HTML mark up above.

If we use an EditorTemplate, the first gotcha that we run into is that, by default, the templates just work on your view model’s property. But in this case, we *also* was the list of items to populate all the radio buttons. It turns out that the EditorFor() methods do give you a way to pass in additional data. There is an overload of the EditorFor() method where the last parameter allows you to pass an anonymous object for “extra” data that you can use in your view – it gets put on the view data dictionary:

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

Now we can create a file called RadioButtonList.ascx that looks like this:

   1:  <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
   2:  <%
   3:      var list = this.ViewData["selectList"] as SelectList;
   4:  %>
   5:  <div>
   6:      <% foreach (var item in list) {
   7:             var radioId = ViewData.TemplateInfo.GetFullHtmlFieldId(item.Value);
   8:             var checkedAttr = item.Selected ? "checked=\"checked\"" : string.Empty;
   9:      %>
  10:          <input type="radio" id="<%: radioId %>" name="<%: ViewData.TemplateInfo.HtmlFieldPrefix %>" value="<%: item.Value %>" <%: checkedAttr %>/>
  11:          <label for="<%: radioId %>"><%: item.Text %></label>
  12:      <% } %>
  13:  </div>

There are several things to note about the code above. First, you can see in line #3, it’s getting the SelectList out of the view data dictionary. Then on line #7 it uses the GetFullHtmlFieldId() method from the TemplateInfo class to ensure we get unique IDs. We pass the Value to this method so that it will produce IDs like “Name_Foo” and “Name_Bar” rather than just “Name” which is our property name. However, for the “name” attribute (on line #10) we can just use the normal HtmlFieldPrefix property so that we ensure all radio buttons have the same name which corresponds to the view model’s property name. We also get to leverage the fact the a SelectListItem has a Boolean Selected property so we can set the checkedAttr variable on line #8 and use it on line #10. Finally, it’s trivial to set the correct “for” attribute for the <label> on line #11 since we already produced that value.

Because the TemplateInfo class provides all the metadata for our view, we’re able to produce this view that is widely re-usable across our application. In fact, we can create a couple HTML helpers to better encapsulate this call and make it more user friendly:

   1:  public static MvcHtmlString RadioButtonList<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, params string[] items)
   2:  {
   3:      return htmlHelper.RadioButtonList(expression, new SelectList(items));
   4:  }
   5:   
   6:  public static MvcHtmlString RadioButtonList<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItem> items)
   7:  {
   8:      var func = expression.Compile();
   9:      var result = func(htmlHelper.ViewData.Model);
  10:      var list = new SelectList(items, "Value", "Text", result);
  11:      return htmlHelper.EditorFor(expression, "RadioButtonList", new { selectList = list });
  12:  }

This allows us to simply the call like this:

   1:  <%: Html.RadioButtonList(m => m.Name, "Foo", "Bar" ) %>

In that example, the values for the radio button are hard-coded and being passed in directly. But if you had a view model that contained a property for the collection of items you could call the second overload like this:

   1:  <%: Html.RadioButtonList(m => m.Name, Model.FooBarList ) %>

The Editor templates introduced in MVC 2 definitely allow for much more flexible views/editors than previously available. By knowing about the features you have available to you with the TemplateInfo class, you can take these concepts and customize your editors with extreme flexibility and re-usability.

posted on Wednesday, May 26, 2010 5:02 PM Print
Comments
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Ryk
6/6/2010 6:17 PM
Excellent, just what i was looking for! Thanks!
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Arnold Smith
9/29/2010 9:49 AM
Hi Steve,

I read your article with interest and implemented the code with some minor changes. Then placed the example code on a page:

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

On a post I looked at System.Web.Mvc.ModelStateDictionary. The key (PagerType) was there but the value was empty. Am I looking in the right place or have I not implemented correctly?

Overall, it looks like I should be able to create a SelectItemList for my Model and then pass that list to the View, with a checked attribute. That's nice.

Thanks!

Arnold
(reformed Web Forms developer)
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Steve
10/1/2010 10:12 PM
@Arnold - I'm not sure I 100% understand your issue. Do you have an html element in your markup with the key "PagerType"? If so, is this your radio button? did you verify the mark up was as you expected when viewing source?
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Not able to get selected item
10/11/2010 2:48 AM
I have used this example with <%: Html.EditorFor(m => m.Name...
syntax but in controller the name property is empty in the formcollection in controller.pls let me know how can i get the selected value from the radiobutton list rendered
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Steve
10/16/2010 10:23 AM
@Not able to get selected item - because they all the the same "name" property, you can get the value of the selected item.
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Steve
10/18/2010 10:17 AM
@Adrian - You're absolutely correct. In this case, you can add the overload to the RadioButtonList() just as you have done above. Next, you have to change the third parameter that you're passing to the EditorFor() method. That is, instead of this:

new { selectList = list }

you pass this:

new { selectList = list, htmlAttr = htmlAttributes }

Now inside the editor, you can extract it from the dictionary in a similar way:

var htmlAttr = this.ViewData["htmlAttr"] as IDictionary;

then use it in your editor.
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Amy
2/1/2011 3:50 PM
Awesome post!
You forgot one thing which had me scratching my head for 30 min or so.

Editor templates need to be placed in Views->Shared->*EditorTemplates*

As disused here
http://www.codecapers.com/post/Display-and-Editor-Templates-in-ASPNET-MVC-2.aspx

Thanks again for all your post. Keep up the great work
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
hax
4/4/2011 2:23 AM
Why wouldn't you have a complete sample? I can't make RadioButtonList, it error at htmlHelper.EditorFor( ...
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Steve
4/5/2011 9:15 AM
@hax - The code shown above is complete - there is no other "missing" code. What is the error you're getting?
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
hax
4/7/2011 5:16 AM
I found problem is missing: using System.Web.Mvc.Html;
and to use syntax <%:Html.RadioButtonList()%>
I must add in web.config
<add namespace="MyProject.MyHtmlHelpers" />
I can run it now. I think you should add the detail to help implement the coding.
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
James Keller
4/26/2011 11:38 AM
Hi Steve

Found this article through Google. Does this work with Telerik?
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Just some person
4/28/2011 11:13 PM
As a newbie to MVC and web coding in general, I'd like to use this code for some sets of radiobuttons in a foreach loop. It's clearly described where the EditorTemplate markup goes but I'm scratching my head as to where the above two static functions are meant to go... Into a separate TemplateInfo class in a file in the model? Should they be part of my repository model? Or should they go into the .ascx file too? What is the best practice? Like others I'd appreciate a complete example :)
I'm still learning this stuff, so thanks for any help!
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Steve
5/2/2011 8:12 AM
@JustSomePerson - You can really put those 2 static functions anywhere. I typically just put them inside a static class called HtmlHelpers - you could put this cs file in the root of your web project.
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
eliman
9/29/2011 6:43 PM
Hi Steve,
great post, need some hint on how to solve a slightly different problem.
I want create editor templates for different questions types (some how similar to google docs forms)
1. I will have multiple choice single answer questions (RadioButtons)
2. Multi choice single answer (checkboxes)
3.....

Thanks,
Eliman
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Steve
9/30/2011 9:26 AM
@eliman - have a look at these and see if they help you out:

http://geekswithblogs.net/michelotti/archive/2010/09/08/mvc-2-model-metadata-to-render-dynamic-ui.aspx

http://geekswithblogs.net/michelotti/archive/2011/01/30/mvc-3-additionalmetadata-attribute-with-viewbag-to-render-dynamic-ui.aspx
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
resume services
10/29/2011 12:59 PM
@james Did you ever get your answer about it working with Telerik?
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
arame
3/1/2012 11:34 AM
I cannot get the selected value.
In my code I have
<%: Html.RadioButtonList(model => model.Employee.CostedFlag, "Uncosted", "Costed" ) %>
In Firebug this translates to
<input id="Employee_CostedFlag" type="radio" value="" name="Employee.CostedFlag">
<label for="Employee_CostedFlag">Uncosted</label>
<input id="Employee_CostedFlag" type="radio" value="" name="Employee.CostedFlag">
<label for="Employee_CostedFlag">Costed</label>
I want to pick up the selected value in jquery, but this does not work;
$(document).ready(function () {
$("#Employee_CostedFlag").change(function () {
if ($("#Employee_CostedFlag").find("input[@checked]").val()) {
$("#CostedRateRow").attr('style', 'dispaly: block');
} else {
$("#CostedRateRow").attr('style', 'dispaly: hide');
}
});
});
How do i fix this?
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
Steve Michelotti
3/1/2012 12:57 PM
@arame - that doesn't look right. the ID's of your radio buttons should be different, the name attributes should be the same. Your jQuery selector should look more like this:

$('input[name=radioName]:checked')
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
arame
3/2/2012 6:09 AM
The problem I had with the code I posted is that I was using RadioButtonList in a similar way to how I use DropDownListFor, which is to populate an id field from a list. In this case I wanted to populate a bool field from 2 options. However now I understand the code better, I need the expression parameter to be a SelectList and not a boolean value. I need to make the boolean field to be a hidden field and populate it from a change event on the radiobuttons. As a result I do not think I can use data annotations to client side validate that the user checked a radiobutton, and I am not sure how else to do this on the client. Maybe there is a neat solution to this, but even if there isn't I am still pleased to see this article here, I have learnt a lot.
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
arame
3/6/2012 6:53 AM
You can delete that last comment. I understand your code better now and I have got it working. Thank you for that!
Gravatar
# re: MVC 2 Editor Template for Radio Buttons
faizel
4/19/2012 8:00 AM
coding is not the problem. but problem pages screenshot is useful to solve the codings.

Post Comment

Title *
Name *
Email
Comment *  
 

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