Search code examples
knockout.jsknockout-mapping-plugin

Knockout radio button binding with boolean


As per this Fiddle I created when you run it it does not bind the value to "no Blue" option as expected.

var viewModel = {    
    isBlue: ko.observable(false)
    //isBlue: ko.observable("false") this works
};

As I have mentioned in there when I pass a string value "false" it does the binding. It's clear that knockout does type comparison as well.

In my application I am using komapper which creates the view model. For properties in boolean it creates bool values and initialises variables in bool. Do I need to convert them to strings? In input radio button element we will always have to work with string values. Can I use a boolean value for checkboxes?

I tried with "checkedValue" still no luck.


Solution

  • I think you're experiencing a bug that has been fixed in a later version of knockout.

    The way it should work (and like you tried):

    <input name="Test" type="radio" data-bind="checkedValue: true, 
                                               checked: isBlue" />Blue
    <input name="Test" type="radio" data-bind="checkedValue: false, 
                                               checked: isBlue" />No Blue
    
    ko.applyBindings({
      isBlue: ko.observable(false)
    });
    

    If you include knockout version 3.4, it works as expected:

    ko.applyBindings({
      isBlue: ko.observable(false)
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
    
    <label>
      <input name="Test" type="radio" data-bind="checkedValue: true, checked: isBlue" /> Blue
    </label>
    <label>
      <input name="Test" type="radio" data-bind="checkedValue: false, checked: isBlue" /> No Blue
    </label>
    <br />
    <strong style="color: green">3.4.0: Does work</strong>

    When you include the version you've used in the fiddle (2.1.0), it doesn't:

    ko.applyBindings({
      isBlue: ko.observable(false)
    });
    <script src="https://cloud.github.com/downloads/knockout/knockout/knockout-2.1.0.js"></script>
    
    <label>
      <input name="Test" type="radio" data-bind="checkedValue: true, checked: isBlue" />Blue
    </label>
    <label>
      <input name="Test" type="radio" data-bind="checkedValue: false, checked: isBlue" />No Blue
    </label>
    <br />
    <strong style="color: red">2.1.0: Does not work</strong>

    Edit: after some more digging: I don't think it's a bug in 2.1.0; the checkedValue didn't even exist at that point!

    Looking at the source you notice the logic for getting the checked value is very different between 2.1.0 and later versions:

    In 2.1.0:

    if (element.type == "checkbox") {
      valueToWrite = element.checked;
    } else if ((element.type == "radio") && (element.checked)) {
      valueToWrite = element.value;
    }
    

    In 3.4.0:

     var checkedValue = ko.pureComputed(function() {
       // Treat "value" like "checkedValue" when it is included with "checked" binding
       if (allBindings['has']('checkedValue')) {
         return ko.utils.unwrapObservable(allBindings.get('checkedValue'));
       } else if (allBindings['has']('value')) {
         return ko.utils.unwrapObservable(allBindings.get('value'));
       }
    
      return element.value;
    });
    

    So a definite answer would be: update to 3.4.0, or create a custom checked binding that implements the 3.4.0 behavior (if a version update would break your project)