07 February 2013

A KnockOut.js binding helper for a JQuery Mobile Slider

For those who have missed it – besides all the things I do on Windows Phone and Windows 8 I actually have a job, and in that job I am currently doing a lot of HTML5/Javascript development – now focusing on a Single Page App using (amongst others) Knockout.js and JQuery Mobile. For those who don’t know: Knockout is a great tool that makes data binding possible in a web environment, making the way I think about making applications – using MVVM – possible in an HTML/Javascript  environment as well. If you use Amplify.js you even have messaging. Oh goody!

Now don’t get me wrong – I am not forsaking XAML and C# (like some others *ahem*), but I tried to find a way to bind a JQuery Mobile slider to Knockout.js. That’s not supported out of the box, so you have to make or find a customer ‘binding helper’ and I found so much examples that were plainly wrong or incomplete that I had to blog mine, which is complete and actually works:

ko.bindingHandlers.jQuerySliderValue = {
  // Initialize slider
  init: function (element, valueAccessor)
  {
    var val = valueAccessor()();
    var el = $(element);
    el.slider({ value: val });

    el.bind("change", function (event, ui)
    {
      var value = valueAccessor()();
      if (value !== el.val)
      {
        valueAccessor()(parseInt(el.val()));
      }
    });
  },

  //handle the model value changing
  update: function (element, valueAccessor)
  {
    var el = $(element);

    var value = ko.utils.unwrapObservable(valueAccessor()());
    if (value !== el.val())
    {
      el.val(value);
      el.slider("refresh");
    }
  }
};

You can use this in HTML like this:

<input type="range" name="slider-1" id="slider-1" 
  min="0" max="5"
  data-bind="jQuerySliderValue: locationDataIndex" />

Now I haven’t all conjured this up out of thin air, there are plenty of samples on, for instance, stackoverflow. Actually my most important addition to the samples out there is the red parseInt piece. All samples seem to fail to take into account that a) data binding can happen both way and b) a Jquery mobile slider apparently returns a string value.  This leads to some interesting results if you try to manipulate the return value in your javascript view model by adding 1 to it. What you think happens is:

0+1=1 , 1+1=2 etc.

But like I said, used this way a range return a string, so you get

“0” + 1 = “01” , “01” +1 = “011”.

And an interesting out of range error. The really gotcha is that it seems to go well the first time, the error only occurs the second time. It seems to make no sense at all. Sure, I could have used input type="number" data-type="range", maybe that would have helped as well, but that ain’t in the samples either. Either way, this fixes it for all cases.

So, this is a very old type of blog post, not so much written from the desire to teach and share but borne from sheer annoyance about the incompleteness and incorrectness of what’s out there – the feeling that actually fuelled the creation of this blog ;-)