asp.net mvc: why is Html.CheckBox generating an additional hidden input
Asked Answered
B

13

240

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" /> 
Berne answered 23/4, 2010 at 8:53 Comment(7)
possible duplicate of Razor ViewEngine HTML.Checkbox method creates a hidden input. Why?Ianthe
ericvg's answer in the possible duplicate question also explains what the model binder does when both the checkbox and hidden fields are submitted.Mors
I hate this thing it was massing up with my jquery.Wasson
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.Omland
If this is really undesired then don't use Html.CheckBox(...) and just input the html for a checkboxMaleeny
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.Tonie
If anybody needs to get this form data and serialize it for async sending, with this code it works correctly: stackoverflow.com/a/22420377Woody
F
220

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.

Felix answered 23/4, 2010 at 8:56 Comment(15)
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.Yours
@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.Felix
It starts with a harmless checkbox and then before you know it we have view state then it evolves into ASP.NET MVCForms.Yours
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 itOrthman
@Felix 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.Yours
@TheMuffinMan: My example? This question is about checkboxes, not ints and strings.Felix
@Felix Check out the second comment, you use an example of initializing a boolean called IsActive to true.Yours
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<string> which I expect to be populated only with the values I needed.Pyrenees
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.Throwaway
@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.Felix
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.Thurstan
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.Esparto
Just change a form method to "GET" and you'll get: ?foo=true&foo=falsePerpetuity
@TheMuffinMan on april 15, 2015 you predicted Razor Pages. Well done.Goatsbeard
@JimYarbro Lol. Someone at Microsoft must have seen my comments.Yours
F
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})
Fruin answered 23/4, 2010 at 8:53 Comment(3)
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.Smart
@Smart Either that, or put your helper in the namespace System.Web.Mvc.Html to be accessible on all viewsRica
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); }Broz
E
18

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

Eloquent answered 1/2, 2021 at 20:31 Comment(0)
G
13

when the check box is checked and submitted perform this

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

Refer

Glottal answered 1/11, 2012 at 3:46 Comment(0)
S
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);
    }
}
Septi answered 20/6, 2017 at 11:48 Comment(0)
P
8

The manual approach is this:

bool IsDefault = (Request.Form["IsDefault"] != "false");
Psychedelic answered 10/10, 2011 at 5:56 Comment(4)
And is this guaranteed to get you the checkbox type value and not the hidden type value?Moses
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'.Psychedelic
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.Embezzle
This got me once, I was checking if the value == truePyrope
E
5

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

bool isChecked = Request.Form["foo"].Contains("true");
Examinant answered 31/5, 2017 at 20:40 Comment(1)
What if the checkbox is initially set? The hidden field would contain "true", no?Deil
M
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);
}
Misleading answered 4/11, 2020 at 7:25 Comment(0)
V
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>
Victual answered 29/3, 2017 at 0:2 Comment(0)
P
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">
Paulino answered 22/5, 2019 at 12:18 Comment(0)
A
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
);
Acquittal answered 9/7, 2022 at 1:44 Comment(0)
A
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')

Anette answered 9/6, 2015 at 15:34 Comment(0)
S
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(...)
Springfield answered 19/10, 2015 at 14:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.