bootstrap-switch-ko table problem

Knockout binding handler example for bootstrap-switch. Handles updates from the view model as well as updates from the view.

by Sourabh Tewari

HTML

<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap-responsive.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/1.8/js/bootstrap-switch.min.js"></script>
<table class="table table-striped table-condensed table-bordered">
    <thead>
    <tr>
        <th>Name</th>
        <th>isSleeping</th>
        <th>Sleep toggle checkbox</th>
        <th>Sleep toggle switch</th>
        <th>Button for comparison</th>
    </tr>
    </thead>
    <tbody data-bind="foreach: items">
        <tr>
            <td data-bind="text: Name"></td>
            <td data-bind="text: sleeping"></td>
            <td>
                <input type="checkbox" data-bind="checked: sleeping" />
            </td>
            <td>
                <div class="switch switch-large" data-bind="bootstrapSwitchOn: sleeping">
                    <input type="checkbox" />
                </div>
            </td>
            <td>
                <button type="button" class="btn btn-primary btn-lg" data-bind="click: $parent.btnClick">Toggle Sleep</button>
            </td>
        </tr>
    </tbody>
</table>

<button data-bind="click:updateById" style="margin: 40px">Toggle sleep status</button>

CSS

/* ============================================================
 * bootstrapSwitch v1.2 by Larentis Mattia @spiritualGuru
 * http://www.larentis.eu/switch/
 * ============================================================
 * Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 * ============================================================ */
 .has-switch {
    display: inline-block;
    cursor: pointer;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    border-radius: 5px;
    border: 1px solid;
    border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
    position: relative;
    text-align: left;
    overflow: hidden;
    line-height: 8px;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    -o-user-select: none;
    user-select: none;
    min-width: 100px;
}
.has-switch.switch-mini {
    min-width: 72px;
}
.has-switch.switch-small {
    min-width: 80px;
}
.has-switch.switch-large {
    min-width: 120px;
}
.has-switch.deactivate {
    opacity: 0.5;
    filter: alpha(opacity=50);
    cursor: default !important;
}
.has-switch.deactivate label, .has-switch.deactivate span {
    cursor: default !important;
}
.has-switch > div {
    display: inline-block;
    width: 150%;
    position: relative;
    top: 0;
}
.has-switch > div.switch-animate {
    -webkit-transition: left 0.5s;
    -moz-transition: left 0.5s;
    -o-transition: left 0.5s;
    transition: left 0.5s;
}
.has-switch > div.switch-off {
    left: -50%;
}
.has-switch > div.switch-on {
    left: 0%;
}
.has-switch input[type=checkbox] {
    display: none;
}
.has-switch span, .has-switch label {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    cursor: pointer;
    position: relative;
    display: inline-block;
    height: 100%;
    padding-bottom: 4px;
    padding-top: 4px;
    font-size: 14px;
    line-height: 20px;
}
.has-switch span.switch-mini,...

JavaScript

/**
 * Knockout binding handler for bootstrapSwitch indicating the status
 * of the switch (on/off): https://github.com/nostalgiaz/bootstrap-switch
 */

ko.bindingHandlers.bootstrapSwitchOn = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        console.log(bindingContext);
        console.log(viewModel);
        $elem = $(element);
        $elem.bootstrapSwitch();
        // Set intial state
        $elem.bootstrapSwitch('setState', ko.utils.unwrapObservable(valueAccessor()));
        $elem.on('switch-change', function (e, data) {
            // Update the model when changed.
            valueAccessor()(data.value);
            bindingContext.$parent.toggleClick(viewModel);
            
        }); 
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var vStatus = $(element).bootstrapSwitch('status');
        var vmStatus = ko.utils.unwrapObservable(valueAccessor());
        if (vStatus != vmStatus) {
            $(element).bootstrapSwitch('setState', vmStatus);
        }
    }
};

var viewModel = {
    
    updateById: function(anId) {
        if (!anId) {
            // pretend it's coming in from the server so providing
            // an id and an update status, pretend anId === 2 for now:
            anId = 2;
        }
        var targetItem = ko.utils.arrayFirst(viewModel.items(), function(item) {
            return item.id === anId;
        });
        // toggling the sleep status for illustration.
        targetItem.sleeping(!targetItem.sleeping());
    },
    
    btnClick: function(anItem) {
        // the buttons pass their model as an argument
        // so it's easy to update by id
        viewModel.updateById(anItem.id);
    },
    toggleClick: function(anItem) {
        alert(ko.toJSON(anItem));
    },
    changeStatusOnServer: function(aVal) {
        // would like an event here that can be used to identify
        // the id of the object...