0

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!

Nico Beemster
  • 590
  • 6
  • 24

3 Answers3

1

I wrote my own based on yours, its a quick mock up so it can have bugs

http://jsfiddle.net/N9uwx/

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);

    }
};
Anders
  • 17,306
  • 10
  • 76
  • 144
0

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.

Nico Beemster
  • 590
  • 6
  • 24
  • I updated mine because of another SO question http://stackoverflow.com/questions/13682691/knockout-js-jquery-range-slider-2-inputs/13683657#13683657 – Anders Dec 03 '12 at 14:45
  • Found a bug, if I change a value in the model that is bound to the range slider, the slider values are not being updated. Will post a fix (if I find one..) – Nico Beemster Dec 03 '12 at 15:00
  • Haven't figured it out yet, here's a fiddle demonstrating the "challenge": http://jsfiddle.net/adr8G/ – Nico Beemster Dec 04 '12 at 11:28
0

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.

ArlanG
  • 888
  • 9
  • 21