JQuery Mobile
JQuery Mobile

Reputation: 6301

Getting Checkbox Value in ASP.NET MVC 4

I'm working on an ASP.NET MVC 4 app. This app has a basic form. The model for my form looks like the following:

public class MyModel
{
    public string Name { get; set; }
    public bool Remember { get; set; }
}

In my form, I have the following HTML.

<input id="Name" name="Name" type="text" value="@Model.Name" />
<input id="Remember" name="Remember" type="checkbox" value="@Model.Remember" />
<label for="Remember">&nbsp;Remember Me?</label>

When I post the form, the Remember value in the model is always false. However, the Name property in the model has a value. I've tested this by setting a breakpoint in the following:

[HttpPost]
public ActionResult MyAction(MyModel model)
{
  Console.WriteLine(model.Remember.ToString());
}

I can't figure it out. Why isn't the Checkbox value getting set?

Upvotes: 132

Views: 356686

Answers (19)

Chris Moschini
Chris Moschini

Reputation: 37967

Crazy idea... Asp.Net MVC should just accept checked checkboxes as "true" when passed to bools in models... .

I think the below - where a ModelBinder accepts the HTML standard "on" to mean true - should've always been the default implementation in Asp.Net MVC. This solution is for the Classic/Non-Core, but, it should be easy to adapt to Core.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace Brass9.Web.Mvc.ModelBinders
{
    public class FixedCheckboxFormModelBinder : System.Web.Mvc.IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (
                // Form POST
                !controllerContext.HttpContext.Request.ContentType.StartsWith
                    ("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)
                /*
                // Note: This is implied - by the way we add this ModelBinder to the global app list (typeof(bool))
                ||
                bindingContext.ModelMetadata.ModelType != typeof(bool)
                */
            )
            {
                return null;
            }

            string name = bindingContext.ModelName;
            var valueProviderResult = bindingContext.ValueProvider.GetValue(name);

            if (valueProviderResult.AttemptedValue == "on")
            {
                var replacementResult = new ValueProviderResult(true, "on", System.Globalization.CultureInfo.CurrentCulture);
                bindingContext.ModelState.SetModelValue(name, replacementResult);
                return true;
            }

            return null;
        }
    }
}

Then enable it in Global.asax.cs, in Application_Start():

ModelBinders.Binders.Add(typeof(bool), new Brass9.Web.Mvc.ModelBinders.FixedCheckboxFormModelBinder());

So, we just build a custom ModelBinder, filter just for Model values expecting a bool coming in over form POST, and passing us the HTML standard "on" - safely limiting its intervention to checkboxes.

It's actually sort of strange trying to apply this fix, because most documentation about ModelBinders is praise with very little in the way of clear how-tos.

Why we solved it this way:

We're migrating an older app to entirely use original Asp.Net MVC (non-Core). Not only would moving all the checkboxes over to @Html.Checkbox... (much wasn't written this way) take a very long time, it also produces a lot of undesirable results, because of the extra, unnecessary hidden input, and the difficulty of migrating pages over. For example, we know there are some pages that have Javascript walking the DOM expecting elements in a specific order, that the hidden input would break, and don't want to comb through every single page looking for these bugs.

Upvotes: 0

Aaron Carter
Aaron Carter

Reputation: 31

If working with FormCollection rather than model, the assignment can be as simple as:

MyModel.Remember = fields["Remember"] != "false";

Upvotes: 3

C Gil
C Gil

Reputation: 44

I read through the other answers and wasn't quite getting it to work - so here's the solution I ended up with.

My form uses the Html.EditorFor(e => e.Property) to generate the checkbox, and using FormCollection in the controller, this passes a string value of 'true,false' in the controller.

When I'm handling the results I use a loop to cycle through them - I also use an InfoProperty instance to represent the current model value being assessed from the form.

So instead I just check if the string returned starts with the word 'true' and then set a boolean variable to true and pass that into the return model.

if (KeyName.EndsWith("OnOff"))
{
    // set on/off flags value to the model instance
    bool keyTrueFalse = false;
    if(values[KeyName].StartsWith("true"))
    {
        keyTrueFalse = true;
    }
    infoProperty.SetValue(processedInfo, keyTrueFalse);
}

Upvotes: 0

Tom Pažourek
Tom Pažourek

Reputation: 10177

If you really want to use plain HTML (for whatever reason) and not the built-in HtmlHelper extensions, you can do it this way.

Instead of

<input id="Remember" name="Remember" type="checkbox" value="@Model.Remember" />

try using

<input id="Remember" name="Remember" type="checkbox" value="true" @(Model.Remember ? "checked" : "") />

Checkbox inputs in HTML work so that when they're checked, they send the value, and when they're not checked, they don't send anything at all (which will cause ASP.NET MVC to fallback to the default value of the field, false). Also, the value of the checkbox in HTML can be anything not just true/false, so if you really wanted, you can even use a checkbox for a string field in your model.

If you use the built-in Html.RenderCheckbox, it actually outputs two inputs: checkbox and a hidden field so that a false value is sent when the checkbox is unchecked (not just nothing). That may cause some unexpected situations, like this:

Upvotes: 5

webdeveloper
webdeveloper

Reputation: 17288

@Html.EditorFor(x => x.Remember)

Will generate:

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

How does it work:

  • If checkbox remains unchecked, the form submits only the hidden value (false)
  • If checked, then the form submits two fields (false and true) and MVC sets true for the model's bool property

<input id="Remember" name="Remember" type="checkbox" value="@Model.Remember" />

This will always send the default value, if checked.

Upvotes: 238

Lulala
Lulala

Reputation: 11

For the MVC using Model. Model:

public class UserInfo
{
    public string UserID { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public bool RememberMe { get; set; }
}

HTML:

<input type="checkbox" value="true" id="checkbox1" name="RememberMe" checked="@Model.RememberMe"/>
<label for="checkbox1"></label>

In [HttpPost] function, we can get its properties.

[HttpPost]
public ActionResult Login(UserInfo user)
{
   //...
   return View(user);
}

Upvotes: 1

Nour Lababidi
Nour Lababidi

Reputation: 444

In my case I was not setting the model property "Remember" in the get method. Check your logic in the controller. You may be doing the same. I hope this help!

Upvotes: 0

Bekir Topuz
Bekir Topuz

Reputation: 231

Use only this

$("input[type=checkbox]").change(function () {
    if ($(this).prop("checked")) {
        $(this).val(true);
    } else {
        $(this).val(false);
    }
});

Upvotes: 21

irfandar
irfandar

Reputation: 1810

Modify Remember like this

public class MyModel
{
    public string Name { get; set; }
    public bool? Remember { get; set; }
}

Use nullable bool in controller and fallback to false on null like this

[HttpPost]
public ActionResult MyAction(MyModel model)
{
    model.Remember = model.Remember ?? false;
    Console.WriteLine(model.Remember.ToString());
}

Upvotes: 0

hideki
hideki

Reputation: 1340

This has been a major pain and feels like it should be simpler. Here's my setup and solution.

I'm using the following HTML helper:

@Html.CheckBoxFor(model => model.ActiveFlag)

Then, in the controller, I am checking the form collection and processing accordingly:

bool activeFlag = collection["ActiveFlag"] == "false" ? false : true;
[modelObject].ActiveFlag = activeFlag;

Upvotes: 2

Dan Saltmer
Dan Saltmer

Reputation: 2165

Since you are using Model.Name to set the value. I assume you are passing an empty view model to the View.

So the value for Remember is false, and sets the value on the checkbox element to false. This means that when you then select the checkbox, you are posting the value "false" with the form. When you don't select it, it doesn't get posted, so the model defaults to false. Which is why you are seeing a false value in both cases.

The value is only passed when you check the select box. To do a checkbox in Mvc use

@Html.CheckBoxFor(x => x.Remember)

or if you don't want to bind the model to the view.

@Html.CheckBox("Remember")

Mvc does some magic with a hidden field to persist values when they are not selected.

Edit, if you really have an aversion to doing that and want to generate the element yourself, you could do.

<input id="Remember" name="Remember" type="checkbox" value="true" @(Model.Remember ? "checked=\"checked\"" : "") />

Upvotes: 77

Surendran
Surendran

Reputation: 11

@Html.EditorFor(x => x.ShowComment)


$(function () {
        // set default value to control on document ready instead of 'on'/'off' 
        $("input[type='checkbox'][name='ShowComment']").val(@Model.ShowComment.ToString().ToLower());
    });

    $("#ShowComment").change(function() {
        // this block sets value to checkbox control for "true" / "false"

        var chkVal = $("input[type='checkbox'][name='ShowComment']").val();
        if (chkVal == 'false') $("input[type='checkbox'][name='ShowComment']").val(true);
        else $("input[type='checkbox'][name='ShowComment']").val(false);

    });

Upvotes: 1

Chris
Chris

Reputation: 342

To convert a value returned from a check box in a form to a Boolean property I used the ValueProviderResult's in build converter in a custom ModelBinder.

ValueProviderResult cbValue = bindingContext.ValueProvider.GetValue("CheckBoxName");
bool value = (bool)cbValue.ConvertTo(typeof(bool));

Upvotes: 3

user586399
user586399

Reputation:

public ActionResult Index(string username, string password, string rememberMe)
{
   if (!string.IsNullOrEmpty(username))
   {
      bool remember = bool.Parse(rememberMe);
      //...
   }
   return View();
}

Upvotes: 0

Pascal Carmoni
Pascal Carmoni

Reputation: 594

For multiple checkbox with same name... Code to remove unnecessary false :

List<string> d_taxe1 = new List<string>(Request.Form.GetValues("taxe1"));
d_taxe1 = form_checkbox.RemoveExtraFalseFromCheckbox(d_taxe1);

Function

public class form_checkbox
{

    public static List<string> RemoveExtraFalseFromCheckbox(List<string> val)
    {
        List<string> d_taxe1_list = new List<string>(val);

        int y = 0;

        foreach (string cbox in val)
        {

            if (val[y] == "false")
            {
                if (y > 0)
                {
                    if (val[y - 1] == "true")
                    {
                        d_taxe1_list[y] = "remove";
                    }
                }

            }

            y++;
        }

        val = new List<string>(d_taxe1_list);

        foreach (var del in d_taxe1_list)
            if (del == "remove") val.Remove(del);

        return val;

    }      



}

Use it :

int x = 0;
foreach (var detail in d_prix){
factured.taxe1 = (d_taxe1[x] == "true") ? true : false;
x++;
}

Upvotes: 0

Yablargo
Yablargo

Reputation: 3596

I just ran into this (I can't believe it doesn't bind on/off!)

Anyways!

<input type="checkbox" name="checked" />

Will Post a value of "on" or "off".

This WONT bind to a boolean, but you can do this silly workaround!

 public class MyViewModel
 {
     /// <summary>
     /// This is a really dumb hack, because the form post sends "on" / "off"
     /// </summary>                    
     public enum Checkbox
     {
        on = 1,
        off = 0
     }
     public string Name { get; set; }
     public Checkbox Checked { get; set; }
}

Upvotes: 1

John Meyer
John Meyer

Reputation: 2356

I ran into a similar issue and was able to get the checkbox value back by using a checkbox, hiddenfor and little JQuery like so:

@Html.CheckBox("isPreferred", Model.IsPreferred)
@Html.HiddenFor(m => m.IsPreferred)

<script>

    $("#isPreferred").change(function () {

        $("#IsPreferred").val($("#isPreferred").val());

    })

</script>

Upvotes: 2

Yini
Yini

Reputation: 711

Okay, the checkbox is a little bit weird. When you use Html helper, it generates two checkbox inputs on the markup, and both of them get passed in as a name-value pair of IEnumerable if it is checked.

If it is not checked on the markup, it gets passed in only the hidden input which has value of false.

So for example on the markup you have:

      @Html.CheckBox("Chbxs") 

And in the controller action (make sure the name matches the checkbox param name on the controller):

      public ActionResult Index(string param1, string param2,
      string param3, IEnumerable<bool> Chbxs)

Then in the controller you can do some stuff like:

      if (Chbxs != null && Chbxs.Count() == 2)
        {
            checkBoxOnMarkup = true;
        }
        else
        {
            checkBoxOnMarkup = false;
        }

I know this is not an elegant solution. Hope someone here can give some pointers.

Upvotes: 4

Robert
Robert

Reputation: 4406

Instead of

 <input id="Remember" name="Remember" type="checkbox" value="@Model.Remember" />

use:

 @Html.EditorFor(x => x.Remember)

That will give you a checkbox specifically for Remember

Upvotes: 5

Related Questions