Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my BootstrapSwitch not working in a knockout table built using a foreach

I'm trying to replace a checkbox in a table with this good looking bootstrap switch.

I'm also using knockout and the binding handler as suggested by this link.

However I cannot get the binding handler to work and or the switch to be clickable in the table built using a knockout for each.

I've replicated the issue in a jsfiddle.

I'm still learning javascript and knockout and just cannot figure out what's going wrong in the binding handler or switch.

Any ideas?

The Html:

<div>
<input type="checkbox" data-bind="checked: on" />
</div>
<div>
    <span data-bind="text: on() ? 'on' : 'off'"></span>
</div>
<div class="switch switch-large" data-bind="bootstrapSwitchOn: on">
    <input type="checkbox" />
</div>

<p>Why doesn't this work in a table?</p>

<table class="table table-striped table-condensed table-bordered">
    <thead>
    <tr>
        <th>Name</th>
        <th>Old</th>
        <th>Old toggle With CheckBox</th>
        <th>Old toggle With Switch</th>
    </tr>
    </thead>
    <tbody data-bind="foreach: items">
        <tr>
            <td data-bind="text: Name"></td>
            <td data-bind="text: Old"></td>
            <td>
                <input type="checkbox" data-bind="checked: Old" />
            </td>
            <td>
                <div class="switch switch-large" data-bind="bootstrapSwitchOn: Old">
                    <input type="checkbox" />
                </div>
            </td>
        </tr>
    </tbody>
</table>

The binding handler and knockout view

/**
 * 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) {
        $elem = $(element);
        $(element).bootstrapSwitch('setState', ko.utils.unwrapObservable(valueAccessor())); // Set intial state
        $elem.on('switch-change', function (e, data) {
            valueAccessor()(data.value);
        }); // Update the model when changed.
    },
    update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        var vStatus = $(element).bootstrapSwitch('status');
        var vmStatus = ko.utils.unwrapObservable(valueAccessor());
        if (vStatus != vmStatus) {
            $(element).bootstrapSwitch('setState', vmStatus);
        }
    }
};

var vm = {
    on: ko.observable(true),

    items: ko.observableArray([
        {Name: "Dave", Old: ko.observable(false)},
        {Name: "Richard", Old: ko.observable(true)},
        {Name: "John", Old: ko.observable(false)}
    ])    
}

ko.applyBindings(vm);
like image 836
Dave Avatar asked Oct 02 '22 22:10

Dave


2 Answers

You were using an older version of bootstrapSwitch (1.3) which automatically executes the plugin on elements with '.switch' class, giving the impression things were working with the custom binding.

I've updated the plugin to the latest version, and am initiating the plugin within the init binding method, eg:

ko.bindingHandlers.bootstrapSwitchOn = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        $elem = $(element);
        $(element).bootstrapSwitch(); // initiate the plugin before setting options
        $(element).bootstrapSwitch('setState', ko.utils.unwrapObservable(valueAccessor())); // Set intial state
        // .. etc
    }
}

Upgrading the latest version of the plugin appears to fix things: https://jsfiddle.net/badsyntax/59wnW/5/

[UPDATE] - I think the root cause of the issue is a race conditional between the plugin initiating itself on DOM ready, and between knockout building the DOM.

This example works with the old version of bootstrapSwitch, as long as you initiate the plugin after binding the viewmodel: https://jsfiddle.net/badsyntax/59wnW/6/

like image 147
badsyntax Avatar answered Oct 12 '22 03:10

badsyntax


Here's the Knockout binding for v3.2.1 of BootstrapSwitch (http://www.bootstrap-switch.org/):

ko.bindingHandlers.bootstrapSwitchOn = {
  init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
    var $elem;
    $elem = $(element);
    $(element).bootstrapSwitch();
    $(element).bootstrapSwitch("state", ko.utils.unwrapObservable(valueAccessor()));
    $elem.on("switchChange.bootstrapSwitch", function(e, data) {
      valueAccessor()(data);
    });
  },
  update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
    var vStatus, vmStatus;
    vStatus = $(element).bootstrapSwitch("state");
    vmStatus = ko.utils.unwrapObservable(valueAccessor());
    if (vStatus !== vmStatus) {
      $(element).bootstrapSwitch("state", vmStatus);
    }
  }
};
like image 30
ProfNimrod Avatar answered Oct 12 '22 04:10

ProfNimrod