Binding true / false to radio buttons in Knockout JS
Solution 1
One option is to use a writeable computed observable.
In this case, I think that a nice option is to make the writeable computed observable a "sub-observable" of your IsMale
observable. Your view model would look like:
var ViewModel = function() {
this.IsMale = ko.observable(true);
this.IsMale.ForEditing = ko.computed({
read: function() {
return this.IsMale().toString();
},
write: function(newValue) {
this.IsMale(newValue === "true");
},
owner: this
});
};
You would bind it in your UI like:
<label>Male
<input type="radio" name="IsMale" value="true" data-bind="checked:IsMale.ForEditing"/>
</label>
<label>Female
<input type="radio" name="IsMale" value="false" data-bind="checked:IsMale.ForEditing"/>
</label>
Sample: http://jsfiddle.net/rniemeyer/Pjdse/
Solution 2
I know this is an old thread, but I was having the same problem and found out a much better solution that was probably added to knockout after this question was officially answered, so I'll just leave it for people with the same problem.
Currently there is no need for extenders, custom binding handlers or computeds. Just provide a "checkedValue" option, it will use that instead of the html 'value' attribute, and with that you can pass any javascript value.
<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: true"/>
<input type="radio" name="a" data-bind="checked:IsChecked, checkedValue: false"/>
Or:
<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 1"/>
<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 2"/>
<input type="radio" name="b" data-bind="checked:Quantity, checkedValue: 3"/>
Solution 3
This works for me:
http://jsfiddle.net/zrBuL/291/
<label>Male
<input type="radio" name="IsMale" value="1" data-bind="checked:IsMale"/>
</label>
<label>Female
<input type="radio" name="IsMale" value="0" data-bind="checked:IsMale"/>
</label>
Solution 4
Once you figure out that the initial match for the radio button wants to match only a string and wants to set the value to a string, it is simply a matter of converting your initial value to string. I had to fight this with Int values.
After you have setup your observables, convert the value to string and KO will do its magic from there. If you are mapping with individual lines, do the conversion in those lines.
In the example code, I'm using Json to map the whole Model in a single command. Then letting Razor insert the value between the quotes for the conversion.
script type="text/javascript">
KoSetup.ViewModel = ko.mapping.fromJS(@Html.Raw(Json.Encode(Model)));
KoSetup.ViewModel.ManifestEntered("@Model.ManifestEntered"); //Bool
KoSetup.ViewModel.OrderStatusID("@Model.OrderStatusID"); //Int
</script>
I use a "Dump it all to the screen" at the bottom of my web page during development.
<h4>Debug</h4>
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
Here are the data values, Before
"OrderStatusID": 6,
"ManifestEntered": true,
and, After
"OrderStatusID": "6",
"ManifestEntered": "True",
In my project, I didn't need to convert Bools, because I'm able to use a checkbox that doesn't have the same frustration.
Solution 5
ko.bindingHandlers['radiobuttonyesno'] = {
'init': function (element, valueAccessor, allBindingsAccessor) {
var stateHandler = function (property, allBindingsAccessor, key, value, checkIfDifferent) {
if (!property || !ko.isObservable(property)) {
var propWriters = allBindingsAccessor()['_ko_property_writers'];
if (propWriters && propWriters[key])
propWriters[key](value);
} else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) {
property(value);
}
};
var updateHandler = function () {
var valueToWrite;
if ((element.type == "radio") && (element.checked)) {
valueToWrite = element.value;
} else {
return; // "radiobuttonyesno" binding only responds to selected radio buttons
}
valueToWrite = (valueToWrite === "True") ? true : false;
var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue); //can be true of false
stateHandler(modelValue, allBindingsAccessor, 'checked', valueToWrite, true);
};
ko.utils.registerEventHandler(element, "click", updateHandler);
// IE 6 won't allow radio buttons to be selected unless they have a name
if ((element.type == "radio") && !element.name)
ko.bindingHandlers['uniqueName']['init'](element, function () { return true });
},
'update': function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
value = value ? "True" : "False";
if (element.type == "radio") {
element.checked = (element.value == value);
}
}
};
Use this binder instead of creating stupid ko computed observables.
Example:
<label>Male
<input type="radio" name="IsMale" value="True" data-bind="radiobuttonyesno:IsMale"/>
</label>
<label>Female
<input type="radio" name="IsMale" value="False" data-bind="radiobuttonyesno:IsMale"/>
</label>
Related videos on Youtube
C.J.
Updated on October 31, 2020Comments
-
C.J. over 3 years
In my view model I have a IsMale value that has the value true or false.
In my UI I wish to bind it to the following radio buttons:
<label>Male <input type="radio" name="IsMale" value="true" data-bind="checked:IsMale"/> </label> <label>Female <input type="radio" name="IsMale" value="false" data-bind="checked:IsMale"/> </label>
The problem I think is
checked
expects a string "true" / "false". So my question is, how can I get this 2-way binding w/ this UI and model?-
Markus Pscheidt over 10 yearsFor Knockout versions >= 3.0 see Natan's answer for a simpler soution than suggested by the accepted answer.
-
-
Artem about 12 yearsthe only problem I see is that when serializing viewmodel to pass to server, you will get integers in observable (instead of booleans). You will need to call
vm.IsMale(!!vm.+IsMale());
before serializing json to send to server (in case server side cannot handle it properly) -
C.J. about 12 yearsSeems like a great solution. I use the mapping pluggin to refresh my VM. Do you know it that would wipe out ForEditing on IsMale?
-
C.J. about 12 yearsI think you are right, but my Javascript knowledge is lacking. Can you please explain to me how !!+ works? I'm not familiar w/ that syntax.
-
Artem about 12 years@C.J. this converts string or number to boolean - chek this jsfiddle.net/nickolsky/6ydLZ ,if string is already bool it will keep it as bool
-
RP Niemeyer about 12 yearsIf you are using the mapping plugin, then you would have to use the
create
callbacks (knockoutjs.com/documentation/…) in the mappings or add the ForEditing computed observable after your data has been initialized (and after new data has been added). -
RP Niemeyer about 12 yearsHere is an alternative that pushes the computed observable creation into a custom binding, which would make it so you don't have to worry about dealing with it in the mapping plugin: jsfiddle.net/rniemeyer/utsvJ
-
C.J. about 12 yearsThanks very much. I think this is a great solution too.
-
xdumaine over 11 yearsUpdated JS, because the managed resource on both of the above jsfiddles are 404'ing: jsfiddle.net/utsvJ/12
-
Greg Ennis about 11 years+1 to using a binding instead of creating an observable on every object
-
Andy Brudtkuhl about 11 years@RPNiemeyer's solution works awesome for me using the mapping plugin
-
Doctor about 11 yearsif you are sending json objects it makes difference.
-
Andrew almost 11 years@RPNiemeyer is definitely the way to go.
-
Siim almost 11 yearsHere is another version that uses extenders instead of bindings: jsfiddle.net/siimv/mQ7CU May come handy if you have different bindings where you have to use boolean's string value.
-
Mikael Östberg over 10 yearsIt doesn't work both ways though. The radio buttons aren't checked when loaded.
-
Artem over 10 yearsit works, this was just old fiddle - fixed it. At some point github does not allow linking of raw files from jsfiddle, so some old fiddles no longer working.
-
Adam Levitt over 10 yearsThis solution definitely works, but it seems like such a hoop to have to jump through! Is binding to true/false with radio buttons that uncommon that it's not part of the knockout core functionality?
-
Natan over 10 yearsLooking at the source, it seems to make a decision to use the checked value here. The 'useCheckedValue' is set to true if the input is a radio or a checkbox with the value as an array. Also, I'm using Knockout 3.0. See if that helps.
-
ZiglioUK over 10 yearsyeah, thanks, I've upgraded to 3.0.0 and now it's there. Still need to wrap my boolean values in a String because the server code was expecting those ;-)
-
Jason Frank over 10 yearsGood info. Two additional points: (1) I found that with pre-v3.0 Knockout I was able to use the
value
binding and then thechecked
binding (in that order), but with 3.0 I had to use thecheckedValue
binding instead of thevalue
binding. (2) pre-v3.0 was picky about requiring thevalue
binding to precede thechecked
binding to function correctly, so I have a feeling that things might also work better in v3.0 in all scenarios if you put thecheckedValue
binding before thechecked
binding, like they show in the docs. -
aruno almost 10 years@ZiglioNZ one way this can fail is if your
checked
setter (or maybe something dependent on it) raises an error - then the correct checkbox isn't checked -
SFlagg over 8 years@RPNiemeyer thanks! that fiddle in the comments is the one that worked for me.
-
wirble about 8 yearsI am using version 3.4 and finds that I still have to put in value="true" and the above to get it to work.
-
Slawomir almost 7 yearsSomebody, please mark this as "the answer" - it's very clean.
-
Stephen McCormick almost 7 yearsOne issue with this is that it leaves the radio button with the "false" unchecked. I really was looking to toggle between the two buttons and visually have it hold the checked. I needed add a method to reset the checked radio buttons.
-
Stephen McCormick almost 7 yearsIgnore the prior comment. Must of had a caching issue. This works like a charm!