Let's put it short: This is my knockout custom binding for putting a checkbox in indeterminate state.
ko.bindingHandlers.nullableChecked = {
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value == null) element.indeterminate = true;
ko.bindingHandlers.checked.update(element, function () { return value; });
}
};
If the initial value is null
everything works fine and checkbox is put in indeterminate state but when I click the checkbox it doesn't seem to update the bound property's value to false/true accordingly. Am I missing something?
You're not calling Init.
Simply proxy the init function for checked in your nullableChecked init function like you did in the update.
ko.bindingHandlers.nullableChecked = {
init: function(element, valueAccessor) {
ko.bindingHandlers.checked.init(element, valueAccessor);
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value == null) element.indeterminate = true;
ko.bindingHandlers.checked.update(element, valueAccessor);
}
};
Without the init, it's never actually setting up a "click" binding on the checkbox to tell knockout that something has changed. If you look at the debug code (http://knockoutjs.com/downloads/knockout-2.2.1.debug.js), you'll see that init uses jQuery to set up a 'click' event on the checkbox to update the observable when the value changes.
ko.bindingHandlers['checked'] = {
'init': function (element, valueAccessor, allBindingsAccessor) {
var updateHandler = function() {
var valueToWrite;
if (element.type == "checkbox") {
valueToWrite = element.checked;
} else if ((element.type == "radio") && (element.checked)) {
valueToWrite = element.value;
} else {
return; // "checked" binding only responds to checkboxes and selected radio buttons
}
var modelValue = valueAccessor(), unwrappedValue = ko.utils.unwrapObservable(modelValue);
if ((element.type == "checkbox") && (unwrappedValue instanceof Array)) {
// For checkboxes bound to an array, we add/remove the checkbox value to that array
// This works for both observable and non-observable arrays
var existingEntryIndex = ko.utils.arrayIndexOf(unwrappedValue, element.value);
if (element.checked && (existingEntryIndex < 0))
modelValue.push(element.value);
else if ((!element.checked) && (existingEntryIndex >= 0))
modelValue.splice(existingEntryIndex, 1);
} else {
ko.expressionRewriting.writeValueToProperty(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 });
},
Edit: Here's a Fiddle: http://jsfiddle.net/cclose/NFfVn/