Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HTML.CheckBox(string) evaluates to two HTML elements instead of one

Tags:

asp.net-mvc

The code

<%=Html.CheckBox("SendEmail") %>

evaluates to two HTML elements when it's rendered

<input id="SendEmail" name="SendEmail" type="checkbox" value="true" /> 
<input name="SendEmail" type="hidden" value="false" /> 

Is this by a bug? Or by design? If it's by design, why?

like image 688
Graviton Avatar asked Dec 17 '08 02:12

Graviton


3 Answers

I was having some trouble this morning with retrieving selected values from a checkbox group in my MVC app. I sent this out to my team and thought I'd share it with everyone else.

When posting back values for checkboxes, the standard behaviour of all browsers is to completely leave out un-checked checkboxes from the post back. So if you have a checkbox that isn’t checked then nothing shows up for this in Request.Form. This is a fairly well-known phenomenon that most developers account for.

In ASP.Net MVC when you use Html.Checkbox, it attempts to get around this and ensure that you have a value posted back (in this case ‘false’) for un-checked checkboxes. This is done by adding a hidden field containing the value ‘false’.

Eg.

<%= Html.CheckBox("Sites", s.Selected, new { value = s.Value })%>

Produces the HTML

<input id="Sites" name="Sites" type="checkbox" value="1" /><input name="Sites" type="hidden" value="false" />

This is all good and well, until you attempt to use checkbox groups. That is more than one checkbox with the same name where the values are sent back in a single comma separated string. MVC can split this string up for you automatically if you define the value as an array (string[] Sites).

Here’s the view code:

<% foreach(var s in Model) { %>
              <li><%= Html.CheckBox("Sites", s.Selected, new { value = s.Value })%>
              <label><%= s.Name %></label>
              </li>
       <% } %>

The appropriate controller action:

[AcceptVerbs(HttpVerbs.Post)] public ActionResult Edit(int Id, string Name, string SubmissionUrl, string[] Sites)

Unfortunately, because the Html.Checkbox produces this hidden field value as well, the resulting array contains not only values for the selected checkboxes but also ‘false’ for every un-checked checkbox. You get an array that looks something like this:

[0] 'false'
[1] 'false'
[2] '110'
[3] '50'
[4] 'false'

Where 'false' is for checkboxes that are not selected, and integers are the values for the checkboxes that are selected.

This can throw your code out quite a bit if you have only integers for the values of your checkboxes and want to define the result as an array of integers, like so:

public ActionResult Edit(int Id, string Name, string SubmissionUrl, int[] Sites)

Which results in an exception being thrown because it can’t convert the string value ‘false’ to an integer.

The solution is very simple, just avoid Html.Checkbox and manually create the checkboxes in your view code like this:

   <% foreach(var s in Model) { %>
          <li><input type="checkbox" name="Sites" value="<%=s.Value%>" <% if (s.Selected) { %>checked="checked"<% } %> />
          <label><%= s.Name %></label>
          </li>
   <% } %>

Hope this helps someone else!

like image 131
Stuntbeaver Avatar answered Sep 27 '22 23:09

Stuntbeaver


When all else fails, read the source code. :) this is from HtmlHelper.cs:

            // Render an additional <input type="hidden".../> for checkboxes. This
            // addresses scenarios where unchecked checkboxes are not sent in the request.
            // Sending a hidden input makes it possible to know that the checkbox was present
            // on the page when the request was submitted.

I'm not exactly sure how useful that is, but at least you know the intention.

like image 20
Craig Stuntz Avatar answered Sep 27 '22 23:09

Craig Stuntz


I think I found something on the web that is directly related to my question.

like image 30
Graviton Avatar answered Sep 27 '22 23:09

Graviton