Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Two-Way Bind a jQuery Mobile Flip Switch to a Knockout.js Variable

DZone's Guide to

How to Two-Way Bind a jQuery Mobile Flip Switch to a Knockout.js Variable

· Mobile Zone
Free Resource

I’m currently building a mobile JavaScript application and I want to render a Yes/No as a flip switch using jQuery Mobile. To track the status of the variable and pass it to the logic of the application, I’m using Knockout.js.

flipswitch

The two libraries play nice together if you update the variable only in one direction, from the UI to the viewmodel -- in this case just as if Knockout was not in the mix, and annotate the select element that is used by jQuery Mobile as base for the flip switch with the data-role="slider" attribute.

<select data-bind="options: ['Yes','No'], value: Partecipating" data-role="slider">
</select>

<script type="text/javascript">
viewModel = {
    Partecipating: ko.observable('Yes'),
};

ko.applyBindings(viewModel);
</script>

You can view the flip switch at work on the following jsfiddle: http://jsfiddle.net/simonech/4HPVm/3/

Why Two-Way Binding Doesn’t Work

If you also want to have two-way binding so that, when changing the value of the variable, the UI gets updated, this code is not enough. Let’s see why.

Knockout.js is an MVVM JavaScript library with two-way binding and automatically updates HTML components as soon as the variable bound to them changes.

jQuery Mobile “enhances” standard HTML components to make them mobile-friendly, and this is done during the first rendering of an HTML page.

If you are a seasoned front-end developer, you might already understand why these two libraries cannot work well together: jQuery Mobile “processes” the DOM only once--at the beginning of the life of the page--so “enhanced” HTML components cannot be updated by simply changing their backing variable because Knockout.js doesn’t know how to update the new UI generated by jQuery Mobile.

Knockout Custom Binding to the Rescue

Luckily, Knockout.js is extensible and can be configured to “learn” how to update custom UI elements by specifying custom bindings.

A custom binding defines how to initialize and how to update a given HTML element. In this post, I’m showing how to create a custom binding specific for this problem. For more information and a better explanation of all the details of custom bindings, please refer to the Knockout.js documentation site.

First, I’m showing you the code and then I’ll comment it. You can also view the working demo on a more comprehensive jsFiddle: http://jsfiddle.net/simonech/EkAmv/10/

ko.bindingHandlers.jqSlider = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        var currentValue = valueAccessor();
        var bindings = allBindingsAccessor();
        if(bindings.options==undefined){
            $(element).slider(currentValue);
        }
        else{
            var called=0;
            bindings.optionsAfterRender = function(option, item){
                called++;
                if(called==2)
                    $(element).slider(currentValue);
            };
        }

    },
    update: function(element) {
        $( element ).slider( "refresh" );
    }
};

Let’s start with the update method of the binding. This is called every time the HTML component has to be updated, and simply forces the refresh of the flip switch element by calling jQuery mobile API.

The init method is a bit more complicated because it handles two scenarios:

  • The first scenario is when the options for the select element are specified “normally” with the option tag (and the options binding is not specified). In this case, the flip switch is created straight away by calling the .slider method.
  • The second scenario is when the options are also coming from the viewmodel and specified via the options binding. In this case, I had to specify a callback (by setting the optionsAfterRender binding) that is executed after each option is rendered, and call the .slider method only after the last option has been rendered in the DOM.

Here the HTML element with the custom binding is applied:

<select data-bind="options: ['Yes','No'], value: Partecipating, jqSlider: { mini: true }" >
</select>

As a parameter of the custom jqSlider binding you can specify any of the parameter of the .slider method, like mini, theme and trackTheme.

Disabling jQuery Mobile Default Behavior on Select Elements

It’s not over yet: jQuery mobile enhances every select element in the page, so we need to change the default selector used to find all selected elements in the page. To do this, we have to override the initSelector for the selectmenu component:

$( document ).on( "mobileinit", function() {
  $.mobile.selectmenu.prototype.options.initSelector = ".mobileSelect";
});

Remember that these lines have to be placed at the top of the page, even before the inclusion of the jQuery Mobile library

Complete Demo

You can also view a demo on this jsFiddle: http://jsfiddle.net/simonech/EkAmv/10/.

Topics:

Published at DZone with permission of Simone Chiaretta, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}