Search code examples
jquery-mobileknockout.jscustom-bindingrangeslider

How to correctly bind and initialize a jQuery Mobile range slider with knockout.js?


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!


Solution

  • 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.