Reputation: 4896
I just discovered a problem with knockout (3.0 and 3.1) and checkboxes
The problem is, change event binding for a checkbox does not occur consistently across different browsers.
See the code below
<table >
<tr>
<td >
<input type="checkbox" data-bind='checked: CheckVal, event: { change: refresh }' />
</td>
<td>Check Value in checked binding: <span data-bind="text: CheckVal"></span>
</td>
</tr>
<tr>
<td></td>
<td>Check Value in changed event binding: <span data-bind="text: CheckValChanged"></span>
</td>
</tr>
</table>
And the javascript
function MainViewModel() {
var self = this;
this.CheckVal = ko.observable(true);
this.CheckValChanged = ko.observable(true);
this.refresh = function() {
self.CheckValChanged(self.CheckVal());
}
}
viewModel = new MainViewModel();
ko.applyBindings(viewModel);
In Firefox, change event binding occurs AFTER the checkbox value was changed, so in change event handler we can read the actual value of the checkbox (that is, if checkbox is checked, the bound field has value = true).
In all other browsers (and I tested with Chrome, IE 11, Opera and Safari), the change event binding seems to occur BEFORE actually changing the value of the bound field (that is, of checkbox is checked, the value read in change handler is false, and if unchecked, the value read is true)
You can see the issue demonstrated at http://jsfiddle.net/bzamfir/su6SW/2/
The problem is not only the fact that the behavior is inconsistent across the browsers, but also that for most browsers it seems counter-intuitive: the event is change, not changing or beforechange. It looks natural to me that it should fire when change is completed, so the bound value should reflect the current checked state, not the previous one.
My problem here, beside the issue itself, is to find a workaround to handle this consistently. My problem is, I need to populate a drop-down based on the value of checkbox, and I choose to do it in change event (which now seems to be a bad choice, giving the issue found).
I think the way to go is to use a computed observable, which I hope should work as expected.
I'm waiting for both confirmation of the bug, and also for suggestions to best handle this situation.
Thanks
Upvotes: 0
Views: 4176
Reputation: 334
What you could do is make use of a knockout subscription to the CheckVal observable, instead of using the change javascript event.
The subscribe event is then fired every time the CheckVal observable changes.
Your MainViewModel would look something like this:
function MainViewModel() {
var self = this;
this.CheckVal = ko.observable(true);
this.CheckValChanged = ko.observable(true);
this.CheckVal.subscribe(function(value) {
self.CheckValChanged(value);
});
}
and your tag can then change to:
<input type="checkbox" data-bind='checked: CheckVal' />
hope this helps.
Upvotes: 5