MV3 Duplicate Query String Values for CheckBox (true,false for boolean)
Asked Answered
A

4

18

I've created a fairly straight forward page with a check box:

@using (Html.BeginForm("MyController", "MyAction", FormMethod.Get))
{
  @Html.CheckBoxFor(x => x.MyCheckBox)
  <input type="submit" value="Go!" />      
}

The URL is populated with the MyCheckBox value twice!? As such:

MyAction?MyCheckBox=true&MyCheckBox=false

It only duplicates the value if the check box is true. If set to false it will only appear once in the query string.

The code above is simplified as I have a couple of drop downs and a textbox on the form which work fine. I don't think there's anything unusual about the code which I've left out from this question.

Has anyone had a similar issue with query string parameters being duplicated?

Astragal answered 14/11, 2011 at 9:33 Comment(0)
N
18

This behaviour is by design of the checkbox control. The standard HTML checkbox control passes no value if it is not checked. This is unintuitive. Instead, the ASP.Net checkbox control has 2 elements, the standard control which is visible and also a hidden control with a value of 'False'.

Therefore, if the checkbox is not checked, there will be one value passed: False.
If it is checked, there will be two values, True and False. You therefore need to use the following code to check for validity in your code:

bool checkboxChecked = Request.QueryString["MyCheckBox"].Contains("True");
Nymphet answered 14/11, 2011 at 9:48 Comment(4)
Interesting stuff, but what if you're particular about the neatness of the URL? It looks a bit daft having the parameter twice!?Astragal
It may be unsightly, but 99% of your userbase won't understand it. If you're really worried about the neatness of your URLs you could pass the data by post and create a specific route for the form.Nymphet
Yes, you're right. I'll leave it as GET and stop being so pedantic. Many thanks Rory.Astragal
pay attention on .ToLower(): bool checkboxChecked = Request.QueryString["MyCheckBox"].ToLower().Contains("true");Dasher
D
4

Accepted answer is correct however in my case in a recent development the MVC behaviour is misleading.

The MVC Html.CheckBox(...) and Html.CheckBoxFor(...) generate an extra input of 'type=hidden' with the same ID as the checkbox control, leading to the duplicate URL parameters. I got around this problem by simply including the mark up desired as follows:

@if(checkTrue){
    <input type="checkbox" id="MyCheckBox" name="MyCheckbox" checked="checked">
}else{
    <input type="checkbox" id="MyCheckBox" name="MyCheckbox">
}

Would be better wrapped upin a helper to use in place of the MVC code so the value check is encapsulated.

As part of my application, the controller maintains sets of query parameters using both form injection and link injection using helpers in order to preserve state (of paging/filtering controls for example) when clicked to navigate within the same controller scope. As a result of this feature, the check box element is always set back to false if the standard MVC helpers are used. It's a good thing I noticed and did not waste much time on this bug.

Diseased answered 28/1, 2014 at 14:19 Comment(1)
Your remark is not correct, ASP.NET MVC generates an extra input of type hidden and not text when using the CheckBoxFor helpers. See #2697799Dhruv
C
2

In my model, I had a collection of checkboxes like so:

public class PrerequisitesViewModel
{
    public List<StudentPrerequisiteStatusViewModel> PrerequisiteStatuses { get; set; }
}

public class StudentPrerequisiteStatusViewModel
{
    public long Id { get; set; }

    public string Name { get; set; }

    public bool IsSelected { get; set; }
}

In order to get everything to bind correctly, I had to actually convert the values from the querystring and parse them manually with the following code:

// fix for how MVC binds checkboxes... it send "true,false" instead of just true, so we need to just get the true
for (int i = 0; i < model.PrerequisiteStatuses.Count(); i++)
{
    model.PrerequisiteStatuses[i].IsSelected = bool.Parse((Request.QueryString[$"PrerequisiteStatuses[{i}].IsSelected"] ?? "false").Split(',')[0]);
}

Alas, it works, but I can't believe this is necessary in MVC! Hopefully, someone else knows of a better solution.

Cyrano answered 19/12, 2016 at 19:16 Comment(1)
i agree, it should be unnecessary in MVC! a querystring with a delimited list of the active checkboxes should suffice e.g. mycheckboxlist=1,5,6,10Kilocalorie
A
2

I solve this issue with use @Html.HiddenFor

<input id="checkboxId" type="checkbox" value="true" onchange="changeCheckboxValue()">
@Html.HiddenFor(m => m.MyCheckBox, new { @id = "hiddenId" } )

<script>
  function changeCheckboxValue() {
    document.getElementById("checkboxId").value = document.getElementById("hiddenId").checked;
  }
</script>
Animalcule answered 5/5, 2020 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.