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

asp.net Mvc

asp.net Mvc Problem Overview


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" /> 

asp.net Mvc Solutions


Solution 1 - asp.net Mvc

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.

Solution 2 - asp.net Mvc

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})

Solution 3 - asp.net Mvc

when the check box is checked and submitted perform this

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

Refer

Solution 4 - asp.net Mvc

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);
    }
}

Solution 5 - asp.net Mvc

The manual approach is this:

bool IsDefault = (Request.Form["IsDefault"] != "false");

Solution 6 - asp.net Mvc

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

Solution 7 - asp.net Mvc

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

bool isChecked = Request.Form["foo"].Contains("true");

Solution 8 - asp.net Mvc

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>

Solution 9 - asp.net Mvc

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);
}

Solution 10 - asp.net Mvc

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">

Solution 11 - asp.net Mvc

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')

Solution 12 - asp.net Mvc

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

public MemberFormModel() {
    foo = true;
}

and in your view :

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionOmuView Question on Stackoverflow
Solution 1 - asp.net MvcLukLedView Answer on Stackoverflow
Solution 2 - asp.net MvcAlexander TrofimovView Answer on Stackoverflow
Solution 3 - asp.net MvcDesmondView Answer on Stackoverflow
Solution 4 - asp.net MvcCiro CorvinoView Answer on Stackoverflow
Solution 5 - asp.net MvcValamasView Answer on Stackoverflow
Solution 6 - asp.net MvcKolazomaiView Answer on Stackoverflow
Solution 7 - asp.net MvcDave DView Answer on Stackoverflow
Solution 8 - asp.net MvcMatthew LockView Answer on Stackoverflow
Solution 9 - asp.net MvcDavid BouškaView Answer on Stackoverflow
Solution 10 - asp.net MvcVDWWDView Answer on Stackoverflow
Solution 11 - asp.net MvcBlueKoreView Answer on Stackoverflow
Solution 12 - asp.net MvcSaïd MezhoudView Answer on Stackoverflow