238

I just noticed that Html.CheckBox("foo") generates 2 inputs instead of one, anybody knows why is this so ?

<input id="foo" name="foo" type="checkbox" value="true" />
<input name="foo" type="hidden" value="false" /> 
Dariusz Woźniak
  • 9,640
  • 6
  • 60
  • 73
Omu
  • 69,856
  • 92
  • 277
  • 407
  • 4
    possible duplicate of [Razor ViewEngine HTML.Checkbox method creates a hidden input. Why?](http://stackoverflow.com/questions/5462967/razor-viewengine-html-checkbox-method-creates-a-hidden-input-why) – Matt Jun 30 '14 at 13:18
  • 1
    [ericvg's answer](http://stackoverflow.com/a/5463032/2615878) in the possible duplicate question also explains what the model binder does when both the checkbox and hidden fields are submitted. – Mike Grove aka Theophilus Jan 23 '15 at 21:32
  • 4
    I hate this thing it was massing up with my jquery. – OKEEngine Mar 11 '15 at 23:25
  • 3
    Weird implementation by mvc. Sending both values doesnt make sense at all. I checked Request.From["myCheckBox"] and its value was true, false. WTF. I had to write the control manually in the view. – Nick Masao Apr 19 '15 at 08:57
  • If this is really undesired then don't use Html.CheckBox(...) and just input the html for a checkbox – wnbates Sep 16 '16 at 14:07
  • People just want to know if the box on the screen is checked or not, not whether the user can clicked on it. A checkbox should always send a value back to the server, that's what's wrong here. – Russell Horwood Apr 13 '18 at 08:20
  • If anybody needs to get this form data and serialize it for async sending, with this code it works correctly: https://stackoverflow.com/a/22420377 – BRap Oct 09 '19 at 09:16

13 Answers13

218

If checkbox is not selected, form field is not submitted. That is why there is always false value in hidden field. If you leave checkbox unchecked, form will still have value from hidden field. That is how ASP.NET MVC handles checkbox values.

If you want to confirm that, place a checkbox on form not with Html.Hidden, but with <input type="checkbox" name="MyTestCheckboxValue"></input>. Leave checkbox unchecked, submit form and look at posted request values on server side. You'll see that there is no checkbox value. If you had hidden field, it would contain MyTestCheckboxValue entry with false value.

LukLed
  • 31,452
  • 17
  • 82
  • 107
  • 60
    If you didn't check the box then obviously the answer is false, no need for it to send back the item. Silly design in my opinion. – The Muffin Man Sep 18 '13 at 23:24
  • 60
    @TheMuffinMan: This is not silly option. Lets say your view model has property called `IsActive`, which is initiated to `true` in constructor. User deselects checkbox, but since value is not sent to server, model binder doesn't pick it up, and property value is not changed. Model binder shouldn't assume, that if value is not sent, it was set to false, because it could be your decision not to send this value. – LukLed Jul 28 '14 at 09:12
  • 26
    It starts with a harmless checkbox and then before you know it we have view state then it evolves into ASP.NET MVCForms. – The Muffin Man Apr 15 '15 at 23:16
  • extra hidden input causing issues, if client modifies the value of the hidden filed, for some reason if we wanted to load the same view with model.. error death page - cannot convert string to boolean !!!how do we handle it – Jay Apr 17 '15 at 06:05
  • 5
    @LukLed Replying to your comment late... I think that logic is flawed because if your view model has a property type int and there is no input on the form then the default value is 0 because no value came through to change it. Same goes with any other property, default value for a string is null. If you don't send a value it's null. In your example of setting a property to true in the constructor, that's honestly a bad design decision and it now comes to light because of how checkboxes work in http land. – The Muffin Man Jan 29 '16 at 19:54
  • @TheMuffinMan: My example? This question is about checkboxes, not ints and strings. – LukLed Feb 01 '16 at 10:02
  • @LukLed Check out the second comment, you use an example of initializing a boolean called IsActive to true. – The Muffin Man Feb 01 '16 at 15:37
  • The problem here is now I have to handle a lot of "false" values prior to get the values I want in hand. I have a List which I expect to be populated only with the values I needed. – dmyoko Jul 22 '16 at 20:39
  • 10
    This shadowing logic breaks for disabled checkboxes - they send `false` value even if they are checked, and that is confusing. Disabled checkboxes should not send any value at all, if ASP.NET wants to be compatible with default HTTP behavior. – JustAMartin Aug 10 '16 at 09:11
  • @JustAMartin: It doesn't matter for disabled checkboxes. If checkbox is disabled, you should not rely on its value on server. If you rely on value of disabled checkbox, you are doing something wrong. – LukLed Aug 10 '16 at 10:31
  • 2
    Would be nice if Microsoft at least add a flag that allows us to disable the hidden field generation, for cases where we know what we're doing and still like to use the CheckBoxFor to generate the property name mapping. – ShuberFu Nov 30 '17 at 16:51
  • https://www.w3.org/TR/html5/forms.html#constructing-the-form-data-set `If any of the following conditions are met, then skip these substeps for this element: ... The field element is an input element whose type attribute is in the Checkbox state and whose checkedness is false.` – Nick Graham Aug 24 '18 at 18:58
  • Just change a form method to "GET" and you'll get: ?foo=true&foo=false – RUKAclMortality May 03 '19 at 10:58
  • @TheMuffinMan on april 15, 2015 you predicted Razor Pages. Well done. – Jim Yarbro Oct 06 '19 at 07:20
  • @JimYarbro Lol. Someone at Microsoft must have seen my comments. – The Muffin Man Oct 09 '19 at 00:16
35

You can write a helper to prevent adding the hidden input:

using System.Web.Mvc;
using System.Web.Mvc.Html;

public static class HelperUI
{
    public static MvcHtmlString CheckBoxSimple(this HtmlHelper htmlHelper, string name, object htmlAttributes)
    {
        string checkBoxWithHidden = htmlHelper.CheckBox(name, htmlAttributes).ToHtmlString().Trim();
        string pureCheckBox = checkBoxWithHidden.Substring(0, checkBoxWithHidden.IndexOf("<input", 1));
        return new MvcHtmlString(pureCheckBox);
    }
}

use it:

@Html.CheckBoxSimple("foo", new {value = bar.Id})
  • 4
    This tripped me up for a minute so just in case... If your helper is in a namespace don't forget to add `@using Your.Name.Space` at the top of your .cshtml razor view file. – ooXei1sh Jul 24 '15 at 16:00
  • 3
    @ooXei1sh Either that, or put your helper in the namespace `System.Web.Mvc.Html` to be accessible on all views – Luke Jul 07 '16 at 16:33
  • 2
    If you want to use this for a form postback or for posting via ajax with the form values then you'll need to handle setting the value to true or false via the onchange event on the checkbox. @Html.CheckBoxSimple(x => Model.Foo, new {value = Model.Foo, onchange = "toggleCheck(this)" }). Then in javascript function ToggleCompleted(el) { var checked = $(el).is(':checked'); $('#Foo').val(checked); } – John81 Jul 06 '18 at 12:15
17

Beginning with ASP.NET (Core) 5, add this to your Startup:

services.Configure<MvcViewOptions>(options =>
{
    // Disable hidden checkboxes
    options.HtmlHelperOptions.CheckBoxHiddenInputRenderMode = CheckBoxHiddenInputRenderMode.None;
});

In your view for example:

<input class="form-check-input" asp-for="@Model.YourBool" />

An additional hidden field for this property is no longer created in your form:

<input class="form-check-input" type="checkbox" data-val="true" data-val-required="The YourBool field is required." id="YourBool" name="YourBool" value="true" />

Source: https://github.com/dotnet/aspnetcore/pull/13014#issuecomment-674449674

Kolazomai
  • 806
  • 7
  • 6
13

when the check box is checked and submitted perform this

if ($('[name="foo"]:checked').length > 0)
    $('[name="foo"]:hidden').val(true);

Refer

Community
  • 1
  • 1
Desmond
  • 1,308
  • 1
  • 19
  • 27
9

This is the strongly typed version of Alexander Trofimov's solution:

using System.Web.Mvc;
using System.Web.Mvc.Html;

public static class HelperUI
{
    public static MvcHtmlString CheckBoxSimpleFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool>> expression, object htmlAttributes)
    {
        string checkBoxWithHidden = htmlHelper.CheckBoxFor(expression, htmlAttributes).ToHtmlString().Trim();
        string pureCheckBox = checkBoxWithHidden.Substring(0, checkBoxWithHidden.IndexOf("<input", 1));
        return new MvcHtmlString(pureCheckBox);
    }
}
Ciro Corvino
  • 2,038
  • 5
  • 20
  • 33
8

The manual approach is this:

bool IsDefault = (Request.Form["IsDefault"] != "false");
Valamas
  • 24,169
  • 25
  • 107
  • 177
  • 1
    And is this guaranteed to get you the checkbox type value and not the hidden type value? – Brian Sweeney Mar 13 '12 at 19:21
  • 1
    that is irrelevant. When the checkbox is not checked, it does not post. So the hiddenbox will have 'false'. If the checkbox is checked the value of 'true, true' is returned (i think), and this does not equal 'false'. – Valamas Jun 12 '12 at 21:33
  • 16
    No, when checkbox is checked, both true and false are posted because both checkbox and hidden fields are valid controls to be sent back. Mvc binder then checks if true value exists for a given namve and if so it prefers true value. You can check this by viewing the posted data. It will have both true and false values against single name. – ZafarYousafi Mar 23 '13 at 14:47
  • This got me once, I was checking if the value == true – Terry Kernan Nov 06 '14 at 11:35
5

Use Contains, it will work with the two possible post values: "false" or "true,false".

bool isChecked = Request.Form["foo"].Contains("true");
Dave D
  • 186
  • 4
  • 5
2

As of 2020/11 and .NET 5 being in preview, there is a pull request that should make this behavior controllable. Thank you guys!

Anyway if someone founds it useful, .NET Core 3.0 port of Alexander Trofimov's answer:

public static IHtmlContent CheckBoxSimple(this IHtmlHelper htmlHelper, string name)
{
    TextWriter writer = new StringWriter();

    IHtmlContent html = htmlHelper.CheckBox(name);
    html.WriteTo(writer, HtmlEncoder.Default);

    string checkBoxWithHidden = writer.ToString();

    string pureCheckBox = checkBoxWithHidden.Substring(0, checkBoxWithHidden.IndexOf("<input", 1));
    return new HtmlString(pureCheckBox);
}
1

I found this really caused issues when I had a WebGrid. The sorting links on the WebGrid would turn by the doubled up querystring or x=true&x=false into x=true,false and cause a parse error in checkbox for.

I ended up using jQuery to delete the hidden fields on the client side:

    <script type="text/javascript">
    $(function () {
        // delete extra hidden fields created by checkboxes as the grid links mess this up by doubling the querystring parameters
        $("input[type='hidden'][name='x']").remove();
    });
    </script>
Matthew Lock
  • 13,144
  • 12
  • 92
  • 130
1

The hidden input was causing problems with styled checkboxes. So I created a Html Helper Extension to place the hidden input outside the div containing the CheckBox.

using System;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;

namespace YourNameSpace
{
    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString CustomCheckBoxFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string labelText)
        {
            //get the data from the model binding
            var fieldName = ExpressionHelper.GetExpressionText(expression);
            var fullBindingName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(fieldName);
            var fieldId = TagBuilder.CreateSanitizedId(fullBindingName);
            var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            var modelValue = metaData.Model;

            //create the checkbox
            TagBuilder checkbox = new TagBuilder("input");
            checkbox.MergeAttribute("type", "checkbox");
            checkbox.MergeAttribute("value", "true"); //the visible checkbox must always have true
            checkbox.MergeAttribute("name", fullBindingName);
            checkbox.MergeAttribute("id", fieldId);

            //is the checkbox checked
            bool isChecked = false;
            if (modelValue != null)
            {
                bool.TryParse(modelValue.ToString(), out isChecked);
            }
            if (isChecked)
            {
                checkbox.MergeAttribute("checked", "checked");
            }

            //add the validation
            checkbox.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(fieldId, metaData));

            //create the outer div
            var outerDiv = new TagBuilder("div");
            outerDiv.AddCssClass("checkbox-container");

            //create the label in the outer div
            var label = new TagBuilder("label");
            label.MergeAttribute("for", fieldId);
            label.AddCssClass("checkbox");

            //render the control
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(outerDiv.ToString(TagRenderMode.StartTag));
            sb.AppendLine(checkbox.ToString(TagRenderMode.SelfClosing));
            sb.AppendLine(label.ToString(TagRenderMode.StartTag));
            sb.AppendLine(labelText); //the label
            sb.AppendLine("<svg width=\"10\" height=\"10\" class=\"icon-check\"><use xlink:href=\"/icons.svg#check\"></use></svg>"); //optional icon
            sb.AppendLine(label.ToString(TagRenderMode.EndTag));
            sb.AppendLine(outerDiv.ToString(TagRenderMode.EndTag));

            //create the extra hidden input needed by MVC outside the div
            TagBuilder hiddenCheckbox = new TagBuilder("input");
            hiddenCheckbox.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
            hiddenCheckbox.MergeAttribute("name", fullBindingName);
            hiddenCheckbox.MergeAttribute("value", "false");
            sb.Append(hiddenCheckbox.ToString(TagRenderMode.SelfClosing));

            //return the custom checkbox
            return MvcHtmlString.Create(sb.ToString());
        }

Result

<div class="checkbox-container">
    <input checked="checked" id="Model_myCheckBox" name="Model.myCheckBox" type="checkbox" value="true">
    <label class="checkbox" for="Model_myCheckBox">
        The checkbox label
        <svg width="10" height="10" class="icon-check"><use xlink:href="/icons.svg#check"></use></svg>
    </label>
</div>
<input name="Model.myCheckBox" type="hidden" value="false">
VDWWD
  • 35,079
  • 22
  • 62
  • 79
1

In .Net core 6, I faced the same issue and I tried @Kolazomai answer and it's working.

using Microsoft.AspNetCore.Mvc;

builder.Services.Configure<MvcViewOptions>(
opt=>opt.HtmlHelperOptions.CheckBoxHiddenInputRenderMode = Microsoft.AspNetCore.Mvc.Rendering.CheckBoxHiddenInputRenderMode.None
);
Darshan Dave
  • 645
  • 2
  • 9
  • 32
0

This is not a bug! It adds the possibility of having always a value, after posting the form to the server. If you want to deal with checkbox input fields with jQuery, use the prop method (pass the 'checked' property as the parameter). Example: $('#id').prop('checked')

josliber
  • 43,891
  • 12
  • 98
  • 133
BlueKore
  • 11
  • 1
0

You can try to initialize the constructor of your Model like that :

public MemberFormModel() {
    foo = true;
}

and in your view :

@html.Checkbox(...)
@html.Hidden(...)