Using Eric Hynds’ multiselect with knockout

Eric Hynds has crafted an awesome enhanced multiselection dropdown as a jQuery UI widget which enhances a normal <select multiple> list. Unfortunately, as many jQuery UI widgets, it manipulates the original HTML when being applied, which renders the normal use of the <select> knockout data-bindings in-effective.

Classical knockout binding for multiselect dropdowns:

<select multiple="multiple" data-bind="
    options: data.selectList,
    optionsText: 'Name',
    optionsValue: 'Value',
    selectedOptions: data.selectedOptions">

This select HTML element is replaced by the UI widget. The user then interacts with a set of buttons, divs, checkboxes, and more.

However, the good news to that is that the UI widget is designed such as to simply hide rather than removing the original element. Even better, when the user selects items in the UI widget, they also get selected in the original <select> element. Therefore with a little trick, we can make use of the knockout bindings for the original select list.

Simply add “multiselect: true” to the data-bind attribute:

<select multiple="multiple" data-bind="
    options: data.selectList,
    optionsText: 'Name',
    optionsValue: 'Value',
    selectedOptions: data.selectedOptions,
    multiselect: true">

We then define a new custom knockout binding called “multiselect”:

ko.bindingHandlers.multiselect = {
    init: function (element) {
        $(element).bind("multiselectclick", function () {
            $.data(element, 'donotrefresh', true);
        });
    },
    update: function (element) {
        var doNotRefresh = !!($.data(element, 'donotrefresh'));
        if (!doNotRefresh) { $(element).multiselect("refresh"); }
        $.data(element, 'donotrefresh', false);
    }
};

The core part to this is the $(element).multiselect(“refresh”), which causes the UI widget to re-build its select list based on the hidden original <select> list, which in turn is updated by knockout based on the model.

The other direction (view -> model) is covered by the UI widget itself: The widget updates the hidden select list, knockout consequently updates the model. There is one minor caveat though: The model update in turn triggers the “update” method of our custom binding. In this case, if we tried and called “refresh” on the UI widget, an error would occur. In order to prevent that, we hook into the click event, set a flag called “donotrefresh” and check it before calling “refresh”.

This way, the “refresh” method on the UI widget is only called if the model was changed from the “outside”, yet is not called if the model was changed due to user interaction with the widget.