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.

Advertisements

5 thoughts on “Using Eric Hynds’ multiselect with knockout

  1. Hi. I’m trying to implement this on my own app. The one thing I’ve noticed is that the update method in the custom binding is called once on load and never again. This is unfortunate as the selectedOptions array is never updated. Maybe you omitted a detail?

  2. Hi Jan, thank you for the comment! Calling update when the model changes is part of the knockout logic. If update is not called there must be a problem with the binding. Are you binding to observables? The selectedOptions must be bound to a ko.observableArray. The same holds for the options binding if the list of available items can change.

    If update IS called but does not refresh the widget because “do-not-refresh” is true, there might be a flaw in the way the flag is maintained in my code.

    If you don’t manage to fix it, can you create a jsfiddle to reproduce the problem?

  3. Couple of things:

    1. Your fix has issues if you are using dynamic templates as the init gets fired before code can actually apply the multiselect() to the select box. You have to do a check in the update part of the binding instead of the init function

    2. This code is one way only. If the knockout model is changed it will push data to the multiselect() but it doesn’t take care of getting the value when the multiselect() is used. The base problem is that the plugin never updates the original select control so that knockout knows there was a value change.

    I did a workaround for now, but i’ll see if I can get a chance to recode it so that it will work cleanly with knockout.

    • Hi jkm,

      thank you for the notice. By the time I wrote the article, multiselect() did update the original select control. Maybe this has changed since? I’ll try to reproduce a.s.a.p.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s