Knockout radio button binding with boolean

那年仲夏 提交于 2019-12-08 18:45:28

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)

Jeroen

Aye. The src binding internally just returns element.value, which for the browser is a string. Check the source file, with this abbreviated/annotated code for getting value associated with being checked:

var checkedValue = ko.pureComputed(function() {
    // Abbreviated, first KO does some stuff for other cases, but
    // in the end it'll do:

    return element.value; // Where `element` is the actual DOM element.
});

Typically, a boolean value would be represented by a checkbox, where you only experience your issue in half of the cases (in which you'd need something like "an inverse checked binding").

Solutions include the one you mention (using strings) as well as using a writeable computed fronting the actual observable, e.g.:

viewModel.viewIsBlue = ko.computed({
  read: function() { return viewModel.isBlue().toLocaleString(); },
  write: function(newVal) { viewModel.isBlue(newVal === 'true'); }
});

You could also possibly go to work with a custom extender with similar functionality if you find yourself repeating the above too often.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!