Question

I have some trouble to get a JQM range slider to work well with knockout. This is a very basic html code for a JQM slider:

<input type="range" name="quantity-slider" id="quantity-slider" min="0" max="10">

I have created as a sample this knockout binding, applied on document ready:

var ViewModel = function() {
    this.quantity = ko.observable(4);
}

$(document).ready(function () {
    ko.applyBindings(new ViewModel());
});

I read over the internet some posts from other people that also found some problems related to the JQM initialization of the range slider (for example here: http://css.dzone.com/articles/knockoutjs-binding-helper and here: http://www.programico.com/1/post/2012/12/knockoutjs-jquerymobile-slider.html) and provide a working solution, each with his own custom binding implementation.

One of them, is as follows (by http://www.hughanderson.com/):

data-bind="value: quantity, slider: quantity"

So far, so good. After that, i run into this problem:

if the JQM slider is on the first page, it works. When the JQM slider is on a second page, is not working anymore.

I think it is an issue related to this particulary JQM widget and his DOM manipulation, as i can understand. To better explain this, i have made two jsFiddle, where i just only swap the order of two JQM pages:

  1. not working: http://jsfiddle.net/5q38Q/ slider on the second JQM page
  2. working: http://jsfiddle.net/5q38Q/1/ slider on the first JQM page

Can someone explain please, which is the right way to initialize the knockout binding for a JQM slider? Maybe there is another way to write a custom binding for the JQM slider, or the knockout binding shall be put in the pagebeforeshow event?

UPDATE: With following change, the slider displays the correct value, and is synchronized also with the text-input part:

$(document).on('pagebeforeshow', '#slider-page', function(){       
    $('#quantity-slider').val(viewModel.quantity());
    $('#quantity-slider').slider('refresh');
});

but im wonder if there is no better solution.

At least, together with Varun's custom binding, it works now for me very well!

Was it helpful?

Solution 2

after some hours spent to test all variations found on the web about this issue, i ended up with following solution, maybe this helps some other people to spare time:

Final working fiddle: http://jsfiddle.net/CT7fy/

The question was about how to integrate knockout.js and the JQuery Mobile slider with multipage navigation, which, i think, will be one of the most common situation.

So i'm sorry that i cannot consider Varun's answer as complete and satisfiyng, but i must say, without Varun's custom bindinghandler i would have never found a working solution.

Here is the custom binding handler, slighlty modified:

/* custom binding handler thanks to Varun http://stackoverflow.com/a/16493161/2308978 */
ko.bindingHandlers.slider = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var value = valueAccessor();
            $(document).on({
                "mouseup touchend keypress": function (elem) {
                var sliderVal = $('#' + element.id).val();
                value(sliderVal);
            }
        }, ".ui-slider");
    }
 };

...and here is the page initialization:

$(document).on('pagebeforeshow', '#slider-page', function(){
    $('#quantity-slider').val(viewModel.quantity());
    $('#quantity-slider').slider('refresh');
});

The text-part of the slider is also synchronized, after you pres enter or tab, as this is the standard slider behavior.

OTHER TIPS

I ran into the same issue. This is how I solved it. Although this solution does not update the observable when you edit the value directly using the text input. (I don't display the input text box, so this solution is sufficient for me)

http://jsfiddle.net/WMr8D/9/

$(document).ready(function () {
var ViewModel = function () {
    var self = this;
    self.quantity = ko.observable(4);
};    

ko.bindingHandlers.slider = {
    init: function (element, valueAccessor) {
        var value = valueAccessor();
        $(document).on({
            "mouseup touchend keypress": function (elem) {
                value($('#' + element.id).val());
            }
        }, ".c-slider");
    }
};    

ko.applyBindings(new ViewModel());
});

In the custom handler, it's more reliable to bind to the "change" event than to bind to mouseup touchend and keypress. Also the pagebeforeshow event is not necessary. You can just set the value attribute during init in the handler:

ko.bindingHandlers.slider = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var value = valueAccessor();
        $(element).attr("value", value());
            $(document).on({
                "change": function (elem) {
                var sliderVal = $('#' + element.id).val();
                value(sliderVal);
            }
        }, ".ui-slider");
    }
 };

See example: http://jsfiddle.net/ZbrB7/2483/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top