Reputation: 590
I am using a Knockout model, and I wish to update values in this model using a jQuery UI range slider. Consider the following example:
Model:
var ViewModel = function () {
var self = this;
self.MinTerm = ko.observable();
self.MaxTerm = ko.observable();
}
ko.applyBindings(new ViewModel());
Now, a jQuery UI range slider should update these two values. This (very ugly) solution came close, the values in my ViewModel are being updated, but somehow this is not reflected in controls bound to these values, such as:
HTML
<div id="sliderTerms"></div>
<span data-bind="text: MinTerm"></span>
<span data-bind="text: MaxTerm"></span>
Script:
$("#sliderTerms").slider({
range: true,
min: 6,
max: 120,
values: [6, 120],
step: 1,
slide: function (event, ui) {
ViewModel.MinTerm = ui.values[0];
ViewModel.MaxTerm = ui.values[1];
}
});
Now, it would be really nice if I could bind to a custom bindingHandler such as this, which works great for binding a slider to a singular value in my KO model:
ko.bindingHandlers.slider = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().sliderOptions || {};
$(element).slider(options);
ko.utils.registerEventHandler(element, "slidechange", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).slider("destroy");
});
ko.utils.registerEventHandler(element, "slide", function (event, ui) {
var observable = valueAccessor();
observable(ui.value);
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (isNaN(value)) value = 0;
$(element).slider("value", value);
}
};
This is where I get stuck. Is it possible to assign an array of valueAccessors, for example, so I can perform something like this:
<div id="sliderTerms" data-bind="rangeSlider: [MinTerm, MaxTerm], sliderOptions: {min: 6, max: 120, range: true, step: 1}"></div>
Thanks!
Upvotes: 0
Views: 3921
Reputation: 1036
for the bug that @Nico Beemster found in his own code, we need to 'update' the update: part like this:
update: function (element, valueAccessor, allBindingsAccessor) {
console.log("update fired");
var value = ko.utils.unwrapObservable(valueAccessor());
if(value instanceof Array)
{
var value1= ko.utils.unwrapObservable(value[0]);
var value2= ko.utils.unwrapObservable(value[1]);
if(value1)
{
value = [value1, value2];
}
else value = 0;
}
else if (isNaN(value)) value = 0;
$(element).slider(value.slice ? "values" : "value", value);
$(element).slider("option", allBindingsAccessor().sliderOptions);
}
here is the updated fiddle. That if you want to update the max and min range elements if they are observable elements.
Upvotes: 0
Reputation: 590
Thanks! Your code inspired me to write this bindingHandler:
ko.bindingHandlers.rangeSlider = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().sliderOptions || {};
var observable = valueAccessor();
ko.utils.registerEventHandler(element, "slidechange", function (event, ui) {
observable[0](ui.values[0]);
observable[1](ui.values[1]);
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).slider("destroy");
});
ko.utils.registerEventHandler(element, "slide", function (event, ui) {
observable[0](ui.values[0]);
observable[1](ui.values[1]);
});
$(element).slider(options);
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if(value instanceof Array)
{
var value1= ko.utils.unwrapObservable(value[0]);
var value2= ko.utils.unwrapObservable(value[1]);
if(value1)
{
value = [value1, value2];
}
else value = 0;
}
else if (isNaN(value)) value = 0;
$(element).slider(value.slice ? "values" : "value", value);
}
};
And now I can bind a rangeSlider to my Knockout model like this:
<div id="sliderTerms" data-bind="rangeSlider: [MinTerm, MaxTerm], sliderOptions: {min: 6, max: 120, range: true, step: 1, values: [6,120]}"></div>
It's a bit smelly, because I assume that values[i] should be bound to valueAccessor[i] but this is good enough for me.
Upvotes: 0
Reputation: 17564
I wrote my own based on yours, its a quick mock up so it can have bugs
ko.bindingHandlers.slider = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().sliderOptions || {};
var observable = valueAccessor();
if(observable().splice) {
options.range = true;
}
options.slide = function(e, ui) {
observable(ui.values ? ui.values : ui.value);
};
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).slider("destroy");
});
$(element).slider(options);
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).slider(value.slice ? "values" : "value", value);
}
};
Upvotes: 1