Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bind multiple radiobuttons to individual booleans

Background

I have a model containing three bools

public class PageDataModel
{
    public bool setting1 { get; set; }
    public bool setting2 { get; set; }
    public bool setting3 { get; set; }
}

If one value is true, the others must be false.

In my view, I am displaying three radio buttons:

Enter image description here

The Razor code for my view:

<div class="row">
        <div class="radio">
            @if (Model.Setting1)
            {
                @Html.RadioButtonFor(m => m.Setting1,  "Setting1", new { Checked = "checked", Name = "Group"})
            }
            else
            {
                @Html.RadioButtonFor(m => m.Setting1, "Setting1", new { Name = "Group"})
            }

            @Html.Label("Setting1")
        </div>
        <div class="radio">
            @if (Model.Setting2)
            {
                @Html.RadioButtonFor(m => m.Setting2, "Setting2", new { Checked = "checked", Name = "Group" })
            }
            else
            {
                @Html.RadioButtonFor(m => m.Setting2, "Setting2", new { Name = "Group"})
            }
            @Html.Label("Setting2")
        </div>
        <div class="radio">
            @if (Model.Setting3)
            {
                @Html.RadioButtonFor(m => m.Setting3, "Setting3", new { Checked = "checked", Name = "Group" })
            }
            else
            {
                @Html.RadioButtonFor(m => m.Setting3, "Setting3", new { Name = "Group"})
            }
            @Html.Label("Setting3")
        </div>
        <button type="submit" class="btn btn-default">Save</button>
    </div>

In my controller, I am returning a model with setting1 set to true:

var model = new PageDataModel
        {
            Setting1 = true,
            Setting2 = false,
            Setting3 = false
        };
        return View(model);

This works correctly, the page is loaded with the 'setting 1' radio button checked.

The problem

When I submit the form, the posted model contains all false values, regardless of which radio button was checked.

I've tried changing the value from "setting1" to true/false, but that had no impact on the returned data.

I'm fairly sure I'm not setting up the binding properly, but I'm not sure where I'm going wrong.

All other examples I have found are binding two radio buttons to one bool value (for example, a yes/no pair of radio buttons bound to one bool). I can change the code to use one int property, but I'd like to solve this using bools.

What am I doing wrong? Am I misusing radio buttons here?

like image 353
Jay Avatar asked Mar 23 '16 12:03

Jay


1 Answers

Technically, this would work fine as long as you have the value set to true. For example:

@Html.RadioButtonFor(m => m.setting1, true)

With that, if the radio is selected, it will post true.

However, the insurmountable problem here is that this is not how radios are designed to function. A radio is part of a group, determined by the value of the name attribute. This causes selecting one radio to deselect all radios with the same name attribute. Using RadioButtonFor, though, will give each radio a different name, based on the property name. In other words, you'd end up with:

<input type="radio" id="setting1" name="setting1" value="true" />
<input type="radio" id="setting2" name="setting2" value="true" />
<input type="radio" id="setting3" name="setting3" value="true" />

Each radio, then, would be part of a different group, consisting of only one radio each (meaning each will be selected by default). You could override the name attribute, but then the posted value would no longer bind to the actual property. For example, if you had:

<input type="radio" id="setting1" name="setting" value="true" />
<input type="radio" id="setting2" name="setting" value="true" />
<input type="radio" id="setting3" name="setting" value="true" />

Such that they're all now part of the same radio group, the value would be posted to setting and it would always be true.

Long and short, there's just no way to make this work like this. What you can do is proxy the value using another property.

public class PageDataModel
{
    public bool setting1 { get; set; }
    public bool setting2 { get; set; }
    public bool setting3 { get; set; }

    public string SelectedSetting
    {
        get
        {
            if (setting1) return "setting1";
            if (setting2) return "setting2";
            if (setting3) return "setting3";
            return null; // or you can set a default here
        }
        set
        {
            switch (value)
            {
                case "setting1":
                    setting1 = true;
                    break;
                case "setting2":
                    setting2 = true;
                    break;
                case "setting3":
                    setting3 = true;
                    break;
            }
        }
    }
}

Then, in your view:

<div class="radio">
    <label>
        @Html.RadioButtonFor(m => m.SelectedSetting, "setting1")
        @Html.DisplayNameFor(m => m.setting1)
    </label>
</div>
<div class="radio">
    <label>
        @Html.RadioButtonFor(m => m.SelectedSetting, "setting2")
        @Html.DisplayNameFor(m => m.setting2)
    </label>
</div>
<div class="radio">
    <label>
        @Html.RadioButtonFor(m => m.SelectedSetting, "setting3")
        @Html.DisplayNameFor(m => m.setting3)
    </label>
</div>

Now, the radios will all be grouped as "SelectedSetting" and will post to the SelectedSetting property. The custom getter and setter on that propery ensures that the proper boolean values will be set on the actual settingX properties.

like image 82
Chris Pratt Avatar answered Oct 04 '22 18:10

Chris Pratt