Reputation: 34489
I've just created a custom knockout binding that seems to be behaving a bit weird and I'm wondering if anyone can help. I've put together a JSFiddle to illustrate:
What I've noticed is that I seem to have to click the toggle switch twice each time to make it switch.
I've isolated this down to the following line in the custom binding (line 27 in the JSFiddle):
valueAccessor()(active);
If you comment this line out, then all of a sudden you need click just the once for the slider to change, obviously the bindings however break in this case.
Can anyone explain what the cause might be, or how I might be able to fix it?
Upvotes: 1
Views: 182
Reputation: 734
The easiest way would be to detach the actual click functionality from the toggle control and have Knockout handle it. That way the observable value will always match the toggle control state. Here is the updated JavaScript that uses this method. I've added a timeout to show that the observable will update the state of the toggle control when the value changes outside of the click event attached to it.
$('.toggle').each(function () {
var self = $(this);
var onText = self.attr("data-on") || "ON";
var offText = self.attr("data-off") || "OFF";
// Go through each item individually so we can get the data-id out
var toggle = $(this).toggles({ text: { on: onText, off: offText }, drag: false, click: false });
});
// Define the knockout custom toggle binding see
// http://knockoutjs.com/documentation/custom-bindings.html
ko.bindingHandlers.toggle = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
// Configure an event to update the observable when the slider is clicked
ko.utils.registerEventHandler(element, "click", function () {
var observable = valueAccessor();
observable( ! observable() );
});
},
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
// This will be called once when the binding is first applied to an element,
// and again whenever the associated observable changes value.
// Update the DOM element based on the supplied values here.
// Grab the value for the item then update the toggle
var value = ko.unwrap(valueAccessor());
if ( true === value ) {
$(element).trigger("toggleOn");
} else {
$(element).trigger("toggleOff");
}
}
};
var viewModel = {};
var settings = {};
settings.test = { value: "test", override: true };
viewModel.settings = ko.mapping.fromJS(settings);
ko.applyBindings(viewModel);
setTimeout( function() {
viewModel.settings.test.override( false );
}, 5000 );
And an updated, working fiddle here: http://jsfiddle.net/6L5Tq/1/
Upvotes: 1
Reputation: 674
The everything will works fine if you comment
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
console.log("update");
var value = ko.unwrap(valueAccessor());
/*if (value)
$(element).trigger("toggleOn");
else
$(element).trigger("toggleOff");*/
}
It will be one side binding, to prevent that you should check that observable value not equal to toggle-plugin state, in update, before firing trigger. But jquery-toggles has very poor functionality and I'm not able to figure state property.
Upvotes: 0