Knockout and jQuery Mobile: Checkboxes

Demonstrates a custom KnockoutJS binding that can be used in place of the standard 'checked' binding when using jQuery Mobile with KnockoutJS.

by mcannon

HTML

<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css">
<script src="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>
<script src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.min.css">
<div data-bind="foreach: people">
    <!--<div>
        <input type="checkbox" data-bind="koChecked: $root.selectedPeople.indexOf(id), attr: {id: 'chk' + ko.unwrap($index) }"/>
        <label data-bind="text: name, attr: {for: 'chk' + ko.unwrap($index) }"></label>
    </div>-->
    <!---->
    <span class="button-checkbox">
        <button type="button" class="btn" data-color="primary"><span data-bind="text: name"></span></button>
        <input type="checkbox" class="hidden" value="" data-bind="value2: id, checkedValue: id, checked: $root.selectedPeople, attr: {id: 'chk' + id }">
    </span>
    <!--
    <li>
        <input type="checkbox" value="" data-bind="value2: id, checkedValue: id, checked: $root.selectedPeople"><span data-bind="text: name"></span>
    </li>-->
</div>

<div>
    <!-- TODO: change data-bind="koChecked: inlist(val1)" 
http://bootsnipp.com/snippets/featured/jquery-checkbox-buttons 
-->
    
<!--    <input class="info" type="checkbox" data-bind="koChecked: val1" id="checkbox1"/>
    <label for="checkbox1">checkbox1</label>
</div>
<div>
    <input type="checkbox" data-bind="koChecked: val2" id="checkbox2"/>
    <label for="checkbox2">checkbox2</label>
</div>
<div>
    <input type="checkbox" data-bind="koChecked: val3" id="checkbox3"/>
    <label for="checkbox3">checkbox3</label>
</div>-->

<hr/>

ViewModel
		<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>

JavaScript

/*ko.bindingHandlers.koChecked = {
    init: ko.bindingHandlers.checked.init,
    update: function (element, valueAccessor) {
        //KO v3 and previous versions of KO handle this differently
        //KO v3 does not use 'update' for 'checked' binding
        if (ko.bindingHandlers.checked.update) 
            ko.bindingHandlers.checked.update.apply(this, arguments); //for KO < v3, delegate the call
        else 
            ko.utils.unwrapObservable(valueAccessor()); //for KO v3, force a subscription to get further updates

        if ($(element).data("mobile-checkboxradio")) //calling 'refresh' only if already enhanced by JQM
            $(element).checkboxradio('refresh');
    }
};*/

function Person(id,name,age) {
    this.id = id;
    this.name = name;
    this.age = age;
}

var listOfPeople = [
    new Person(1, 'Fred', 25),
    new Person(2, 'Joe', 60),
    new Person(3, 'Sally', 43),
    new Person(4, 'Steve', 43),
    new Person(5, 'Bob', 43),
    new Person(6, 'Roger', 43),
    new Person(7, 'Mary', 43)
];

var viewModel = {
    selectedPeople: ko.observableArray([]),
    people: ko.observableArray(listOfPeople),
    checkedList : ko.observableArray([])
    
};

$(function () {
    ko.applyBindings(viewModel);
});

$(function () {
    $('.button-checkbox').each(function () {

        // Settings
        var $widget = $(this),
            $button = $widget.find('button'),
            $checkbox = $widget.find('input:checkbox'),
            color = $button.data('color'),
            settings = {
                on: {
                    icon: 'glyphicon glyphicon-check'
                },
                off: {
                    icon: 'glyphicon glyphicon-unchecked'
                }
            };

        // Event Handlers
        $button.on('click', function () {
            $checkbox.prop('checked', !$checkbox.is(':checked'));
            $checkbox.triggerHandler('change');
            updateDisplay();
        });
        $checkbox.on('change',...