Ok I've been trying to unravel this mess for a few hours now and have gotten nowhere, analogous to a dog chasing its tail. Here's the situation.
I'm using Knockout.js for my UI, which works great by itself. However, I'm trying to use some third party code that makes dropdowns and checkboxes look all pretty. Actually I'm not even sure if this is a third party library or just something our designers wrote. This code hides the real checkbox and replaces it with a fake <span />
that mimics a checkbox through CSS. The click
event of the span triggers the change
event of the real checkbox:
// this code updates the fake UI
this._changeEvent = function() {
self.isChecked = self.$input.is(':checked');
self._updateHTML(false, true);
jQuery(self).trigger('change');
};
// when the user clicks the fake checkbox, we trigger change on the real checkbox
this.$fake.on('click', function(e) {
e.preventDefault();
self.$input.click().trigger('change');
});
// Bind _changeEvent to the real checkbox
this.$input.change(this._changeEvent);
This actually works with Knockout.js, since Knockout will listen to that event handler. In other words, when the user clicks the fake checkbox, the bound Knockout model gets updated. However, what does not work is updating the model. If I call:
model.SomeValue(!curValue); // SomeValue is bound to a checkbox, flip its value
The model gets updated, but the fake UI is not updated. I've traced this problem down to the code in ko.bindingHandlers.checked.update
which does the following:
// When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
element.checked = value;
Basically, the element.checked
property is set, but no events are fired. Thus, the _changeEvent
function is never called. So, I've implemented my own ko.bindingHandlers.checked.update
function, which is a copy of the built-in one. In theory, this is all I should need to do:
ko.bindingHandlers.checked.update = function (element, valueAccessor)
{
var value = ko.utils.unwrapObservable(valueAccessor());
if (element.type == "checkbox")
{
if (value instanceof Array)
{
// When bound to an array, the checkbox being checked represents its value being present in that array
element.checked = ko.utils.arrayIndexOf(value, element.value) >= 0;
}
else
{
// When bound to anything other value (not an array), the checkbox being checked represents the value being trueish
//element.checked = value;
$(element).prop('checked', value).trigger('change'); // <--- this should work!
}
}
else if (element.type == "radio")
{
element.checked = (element.value == value);
}
};
Rather than setting element.checked
, I instead call .prop('checked', value)
and trigger the change event. However, this is not working. Here's what I know so far:
$(element).prop('checked', value).trigger('change');
works perfectly fine. So, knockout.js is screwing with the event some how. Is it unbinding that event handler?$(element)
is the same thing as this.$input
in the fake checkbox binding code. I can set other expando properties on this element, and they show up.change
event handler with its own internal one, and existing bindings were removed. I haven't found a way to confirm this yet.My Question: Mainly, I'm looking for a solution to this problem. Does Knockout.js remove existing change
events that were there before the model was applied? What are the next steps in debugging this code and figuring out exactly what's going on?
Knockout out provides a sophisticated way to use jQuery methods with your binding.
Why Use KnockoutJS? KnockoutJS library provides an easy and clean way to handle complex data-driven interfaces. One can create self-updating UIs for Javascript objects. It is pure JavaScript Library and works with any web framework.
Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model.
Knockout. js was used to build such popular websites as Ancestry.com, Vogue, and Microsoft Azure portal. Thanks to its MVVM model, it's perfect for creating rich and responsive user interfaces with a clean, underlying data model. Today, Knockout.
Rather than overriding the base checked handler, just create your own custom binding to update the UI.
Here's an example of a model and a custom binding to handle updating the UI:
var Model = function () {
this.checked = ko.observable(false);
};
ko.bindingHandlers.customCheckbox = {
init: function (element) {
// Create the custom checkbox here
$(element).customInput();
},
update: function (element) {
// Update the checkbox UI after the value in the model changes
$(element).trigger('updateState');
}
};
I'll bind the model to the following HTML:
<input type="checkbox" name="genre" id="check-1" value="action" data-bind="checked: checked, customCheckbox: checked" />
And really that's all I'll need to do.
Here's an example: http://jsfiddle.net/badsyntax/4Cy3y/
[edit] - i think i rushed through reading your question and didn't get the crux of what you were asking. I'll leave my answer here in any case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With