1

I'm using bootstrap-switch together with the knockout binding handler referenced from this question shown below:

ko.bindingHandlers.bootstrapSwitchOn = {
init: function (element, valueAccessor, allBindingsAccessor, 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);
    }); 
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
    var vStatus = $(element).bootstrapSwitch('status');
    var vmStatus = ko.utils.unwrapObservable(valueAccessor());
    if (vStatus != vmStatus) {
        $(element).bootstrapSwitch('setState', vmStatus);
    }
}
};

This seems to be working quite nicely and I've mocked up a fiddle to illustrate how I'm using it here:

http://jsfiddle.net/swervo/of0q42j0/5/

However, I have a few issues which I can't seem to solve in a satisfactory manner:

1) If I have an array of items in an ko.observable array I can put a click handler on all of them and have them call a function in the parent view model like this:

data-bind="click: $parent.clickHandler"

Which, when called, passes through the items own view model. This is really convenient for getting properties of the item that was clicked, eg., id. I've put a button in the fiddle above to illustrate how easy this is to do.

However, if I'm using the bootstrap-switch instead of a simple button the switch doesn't seem to know about it's parent and I can't find an elegant way of passing through the view model containing the switch to its parent - like you can with a button. I have tried giving each item in the array a reference to it's parent view model and this does work but creates a circular reference and thus doesn't seem like the correct approach.

2) In the application that I'm building the state of items in a list can be changed on a different clients - and the local state needs to update to reflect these remote clients. Equally the state can also be changed on the local client which is then propagated to other clients. My problem here is how to disambiguate between changes to state that have happened locally (ie., due to the user clicking on the switch), and changes that have happened remotely (ie., due to an update coming from the server). In my actual project I'm using knockout subscribe to listen for changes in the values linked to the switches like this:

viewModel.observableValue.subscribe(function(newValue) {
    // test value on server and if it is different update
});

I want to avoid receiving an update from the server and then updating the server again (with the same state) when my switch changes to reflect the new state. At the moment I've fixed this by testing the server state (as implied in the code snippet above) before I send the update and if it is the same as the pending state update I discard it. (I've simulated a server update using a button in the referenced fiddle above).

Neither of my solutions to these problems feel elegant hence the question here. Any help would be much appreciated.

Community
  • 1
  • 1
swervo
  • 749
  • 6
  • 15

1 Answers1

0
  1. I'm not sure what you mean by the 'the switch doesn't seem to know about it's parent'. Looking at http://knockoutjs.com/documentation/custom-bindings.html, I can see that init and update both have a 5th param, bindingContext that has the parent information, should you wish to access it.
  2. Ahem, one of the projects we worked on the past had a toggle button that suffered from the same issue and it was fixed is a very simple way. For events that are generated locally, just attach a property to the object, like .local = true; and check for it in the update (or attach it in your REST handler) to distinguish local/vs REST. Don't forget to delete the property from the view model once done in update though.
  • Thanks for your response. I'd overlooked the bindingContext parameter. I've updated my fiddle here: http://jsfiddle.net/swervo/of0q42j0/6/ And now I can get the switch to report its view model to the parent. But I'm still not clear how to solve part 2 of my question? Where should I attach the .local = true property? – swervo Feb 18 '15 at 20:10