Jquery fix ASP.Net MVC checkbox “true,false” value

Update: After implementing this fix into my application, I discovered a vast performance hit on a page that contained hundreds of checkboxes. Due to that issue and general concerns about manipulating the form, I have opted to simply not use the MVC Checkbox helper and just check for null in the postback. But for most situations this solution still works

The Issue:

ASP.NET MVC checkboxes return a form value of “true,false” if checked and “false” if unchecked.

The Reason:

A checkbox represents a boolean, so it makes logical sense that when you create a checkbox like so:

<input type=”checkbox” name=”myBoolean” />

that a post value would appear with a name of “myBoolean”, and a string “true” or “false” representing the checked/unchecked state of the checkbox at the time of submission. But browsers have a strange way of interpreting a checkbox value. Rather than sending a false value in the event of an unchecked checkbox, the item is excluded from the post array completely. This can complicate life for the server if you are dynamically looping through the post array or are working with a strict language that doesn’t handle nullable booleans well.

Enter MVC

The ASP.Net MVC checkbox helper, implemented as so:

<%: Html.CheckBox(“myBoolean”)%>

creates two inputs with the same name: a standard checkbox and a hidden field with a value of false:

<input id=”myBoolean” name=”myBoolean” type=”checkbox” value=”true” />
<input name=”myBoolean” type=”hidden” value=”false” />

This solves the problem of the vanishing post by ensuring a false value is sent by the hidden field, but introduces a new problem because both values are sent when the checkbox is checked, resulting in a value of “true, false”. This is confusing and ugly and causes casting bugs and general confusion. So to summarize, before the MVC fix, half of all submitted checkboxes provided logical values. After the MVC fix,the other half of all submitted checkboxes provide logical values.

But what about checkbox lists?

This is where things get tricky. Technically, there’s no real checkbox list control, but as we’ve seen, if you give multiple inputs the same name, the server glues their values together in a comma separated list. In the case of checkboxes, only checked checkboxes are included. So the input below will create a post item witha key of “fruitList” and a value of “apple,orange”.

<input name=”fruitList” type=”checkbox” value=”apple” checked=”checked” />
<input name=”fruitList” type=”checkbox” value=”orange” checked=”checked” />
<input name=”fruitList” type=”checkbox” value=”banana” />

So this leaves an unresolved question about how to handle a list of unchecked checkboxes. Should there be a single value of “false” or simply nothing? Personally, I think a single checkbox represents a boolean and should contain a value of “true” or “false”, whereas a list should just be empty. So in the event that all checkboxes are deselected, the list is totally excluded and you’re back to having to check the existance of the form value. It’s a judgement call and the code could quickly be modified to enter a single value of “false” in the scenario of any empty list.

My JQuery Fix

$(window).load(function () {
	$("input:checkbox").each(function () {
		fixHiddenCheckField(this);
	});

	$("input:checkbox").change(function () {
		fixHiddenCheckField(this);
	});
});

function fixHiddenCheckField(checkField) {
	var name = $(checkField).attr("name");
	var hiddenSelector = "input:hidden[name=" + name + "]";
    var checkBoxesInListSelector = "input:checkbox[name=" + name + "]";
    var checkedCheckBoxesInListSelector = "input:checked[name=" + name + "]";

    if ($(checkedCheckBoxesInListSelector).length >= 1 || $(checkBoxesInListSelector).length > 1) {
		$("input").remove(hiddenSelector);
	}
	else {
		if ($(hiddenSelector).length == 0)
			$(checkField).parents("form").append("<input type='hidden' name='"+name+"' value='false' />");
	}
}

The trick is that we’re removing the hidden field when checking the box, then adding a duplicate one back to the parent form when unchecked. I am binding the function to the change event but also using the .each() method to set the initial values. To handle lists, I’m also checking the total length of checkboxes with this name. If there is more than one, I assume this is a list, so I always remove the hidden fields. If you want an unchecked list to produce a single value of false, simply don’t check for $(checkBoxesInListSelector).length > 1 in the if statement.

This entry was posted in asp.net, javascript, mvc. Bookmark the permalink.

4 Responses to Jquery fix ASP.Net MVC checkbox “true,false” value

  1. John Pencola says:

    Nicely written and well explained man! The exclusion of the key/value pairs for certain controls in form posts certainly do seem counter intuitive. When I first learned of this, I thought it made no sense either! But consider thinking about it as the W3C has recommended it specified and ultimately how user-agents have implemented it: http://www.w3.org/TR/html4/interact/forms.html#successful-controls

    Part of what I think makes it appear illogical is mentally modeling a checkbox control in HTML to a Boolean field in a database. I believe the W3C made wise decisions like, chose the default value for such controls as “on” when checked and allow you to specify your own values, to illustrate just that distinction. So then why are empty input text field keys included in posts?? If you check out the recommendation, the W3C leaves that up to the user agent as well (ie: “If a control doesn’t have a current value when the form is submitted, user agents are not required to treat it as a successful control.”) – and only successful controls are included in the post hash. So, rather than worrying about how to pass those states from the form post (cross-browser even), I think it makes sense to put the logic in the program that is receiving the post data to handle the data in your application specific way.

    I think you were onto something when you wrote “…whereas a list (of checkboxes) should just be empty”. Ultimately…. I’ve had too much coffee and 5-HTP today :S

    -JP

  2. S&S says:

    Very fascinating thoughts, young Jedi! Can’t argue with the godfathers of the web. A checkbox fits nicely with a boolean, but it’s good to consider how the browser views it as a single key/value pair that you’re choosing to send or not. Perhaps it’s better to equate a checkbox group to a multiselect listbox (and radio group to a dropdown list) in terms of browser interpretation. But a multiselect box with a single item would be some pretty horrific UI!

    More importantly, thanks for reminding me to go back and review the W3C form spec. Every developer should do so and I forwarded it off to my whole dev team. I learned in my trials on this post that I couldn’t disable a hidden field to exclude it from the post, when it was written right there! We often get caught up in the shiny new stuff and forget the importance of the basics. This is even more necessary when working with a framework that manipulates the DOM to create a false reality (although not nearly as bad as .NET WebForms!). Microsoft is great at providing wheelchairs for people learning to walk.

  3. John Pencola says:

    Glad that this came up from your original post, that’s what its all about! “We often get caught up in the shiny new stuff and forget the importance of the basics” Well put. :)
    -JP

  4. John Hayford says:

    Warning: file(http://svn.wp-plugins.org/devformatter/branches/langs/c#.php): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /home/mindstor/www/www/blog/wp-content/plugins/devformatter/devgeshi.php on line 100

    Warning: implode(): Invalid arguments passed in /home/mindstor/www/www/blog/wp-content/plugins/devformatter/devgeshi.php on line 100

    Warning: file(http://svn.wp-plugins.org/devformatter/branches/langs/c#.php): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /home/mindstor/www/www/blog/wp-content/plugins/devformatter/devgeshi.php on line 100

    Warning: implode(): Invalid arguments passed in /home/mindstor/www/www/blog/wp-content/plugins/devformatter/devgeshi.php on line 100

    Warning: file(http://svn.wp-plugins.org/devformatter/branches/langs/c#.php): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /home/mindstor/www/www/blog/wp-content/plugins/devformatter/devgeshi.php on line 100

    Warning: implode(): Invalid arguments passed in /home/mindstor/www/www/blog/wp-content/plugins/devformatter/devgeshi.php on line 100

     Java |  copy code |? 
    01
    public partial class Boolean
    02
            {
    03
                public static bool ParseStupid(string input)
    04
                {
    05
                    if (String.IsNullOrEmpty(input))
    06
                    {
    07
                        return false;
    08
                    }
    09
                    else
    10
                    {
    11
                        if (input == "true,false" || input == "true")
    12
                        {
    13
                            return true;
    14
                        }
    15
                        else
    16
                        {
    17
                            return false;
    18
                        }
    19
                    }
    20
                }
    21
            }


    lets me do

     c# |  copy code |? 
    1
    
    
    2
     machineInspectionForm.CutPieceFinish = Boolean.ParseStupid( form["CutPieceFinish"]);

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>