Problem with jQuery UI Multiselect plugin

4,490 views
Skip to first unread message

Ω Alisson

unread,
Dec 3, 2010, 6:25:35 AM12/3/10
to knock...@googlegroups.com
I'm trying to use jQuery UI Multiselect plugin(http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/) with Knockout, but the enhanced multiselect doesn't update the observable array, is there something I can do to notify it the value has changed?

mkidder

unread,
Dec 6, 2010, 11:37:23 AM12/6/10
to KnockoutJS
Hi Alisson,

Nice plugin. I didn't have anything better to do this weekend so I
played around and hooked up Knockout. Good opportunity for me to
learn a little ko goodness.

Since the widget makes changes to the original select list via
javascript (no user events to raise), we need to hook into the widgets
custom events. The UI MultiSelect Widget provides all the events out
of the box to do what we need. We wire up the "click", "checkAll",
and "uncheckAll" events.

HTML: >>>>>>>>>>>

<h2>UI MultiSelect Widget -- Test</h2>

<div class="readout">
<h3>What's in the model?</h3>
<table>
<tr>
<td class="label">Multi-selected options:</td>
<td data-bind="text: selectedValues"></td>
</tr>
</table>
</div>

<div>
<select id="example" name="example" multiple="multiple">
</select>
</div>

<<<<<<<<<<<<

JS: >>>>>>>>>>>

var viewModel = {
optionValues : ['Alpha', 'Beta', 'Charlie', 'Delta', 'Eagle'],
selectedValues : ko.observableArray(['Alpha'])
};

$('#example').attr('data-bind','options: optionValues,
selectedOptions: selectedValues');

ko.applyBindings(viewModel);

$('#example').multiselect({
click: function(event, ui){
/*
event: the original event object

ui.value: value of the checkbox
ui.text: text of the checkbox
ui.checked: whether or not the input was checked
or unchecked (boolean)
*/

if(ui.checked &&
viewModel.selectedValues.indexOf(ui.value) < 0){
viewModel.selectedValues.push(ui.value);
}
else if(!ui.checked) {
viewModel.selectedValues.pop(ui.value);
}
},
checkAll: function(){
$('#example').multiselect('getChecked').each(function(){
if(viewModel.selectedValues.indexOf(this.value) < 0)
{
viewModel.selectedValues.push(this.value);
}
});
},
uncheckAll: function(){
viewModel.selectedValues([]);
}

});

<<<<<<<<<<<

Hope the information helps! The code has not been thoroughly tested.
If you find a better way, please let me know. :)

Ω Alisson

unread,
Dec 6, 2010, 11:48:28 AM12/6/10
to knock...@googlegroups.com
Hi Mike, thanks for your attention. I forgot to add my approach here now that it's working.

Your code is good but doesn't sync when the observableArray has elements added with something like viewModel.selectedValues.push("something");

I forked the multiselect plugin to add a simple function required to sync Knockout with it, setChecked(array_of_options)

Also, I stumbled across a bug with large selects on Knockout, it refreshes the options list and lose focus of the last element added, bringing an unpleasant experience. I did a small workaround, it can be found here: https://github.com/thelinuxlich/knockout

Now the code:

VM.selectedValues.subscribe(function(value) {
    $("#example").multiselect("setChecked",VM.selectedValues());
});

$("#example").multiselect({
click: function(event,ui) {
            var options = $.map($("#example").multiselect("getChecked"),function(a) {return $(a).val()});
            VM.selectedValues(options);
        }
    });

I didn't need the checkAll/uncheckAll feature so I didn't wrote custom events for them. Also, the code breaks MVVM(I need to reference "#example" on selectedValues subscription), but that was my take :(

2010/12/6 mkidder <mikek...@gmail.com>

mkidder

unread,
Dec 6, 2010, 11:48:20 PM12/6/10
to KnockoutJS
Yes, I forgot to mention the plugin would not update when the
viewModel selectedValues were changed via some programmatic method.
There wasn't a method to call on the plugin api. Nice contribution.

Do you have to call VM.selectedValues()? The "value" parameter is the
array. I did it like this:

VM.selectedValues.subscribe(function(value) {
$("#example").multiselect("setChecked",value);
});

One change you may want to make in the "setChecked" method for the
MultiSelect widget.

- this.uncheckAll()
+ this._toggleChecked(false);

Calling the uncheckAll method raises the event and can have side
effects. If you combine with my code to handle that event, the Call
Stack will go to infinity and beyond. :)

-Mike
> 2010/12/6 mkidder <mikekid...@gmail.com>

Ω Alisson

unread,
Dec 7, 2010, 5:33:08 AM12/7/10
to knock...@googlegroups.com
You are right, I corrected the event thing and the subscription :)


2010/12/7 mkidder <mikek...@gmail.com>

x00...@gmail.com

unread,
Dec 24, 2012, 4:57:17 AM12/24/12
to knock...@googlegroups.com
Here is my binding and it works at leas for my scenarios:
html:
<select multiple="multiple"
        data-bind="multiselect  : { config : multiSelectConfig,
                                    options: options,
                                    selectedOptions : selectedOptions }" />

js:
ko.bindingHandlers.multiselect = {
    init: function (element, valueAccessor, allBindingAccessors) {
        var options = valueAccessor();

        ko.bindingHandlers['options'].update(
            element,
            function() { return options.options; },
            allBindingAccessors
        );

        ko.bindingHandlers['selectedOptions'].init(
            element,
            function() { return options.selectedOptions; },
            allBindingAccessors
        );

        ko.bindingHandlers['selectedOptions'].update(
            element,
            function() { return options.selectedOptions; },
            allBindingAccessors
        );

        $(element).multiselect(options.config).multiselectfilter({ autoReset: false });

        //make view model know about select/deselect changes
        $(element).bind("multiselectcheckall", function (event, ui) { $(element).trigger("change"); });
        $(element).bind("multiselectuncheckall", function (event, ui) { $(element).trigger("change"); });
    },
    update: function (element, valueAccessor, allBindingAccessors) {
        var options = valueAccessor();
        //update html if items set through code
        ko.bindingHandlers['selectedOptions'].update(
            element,
            function () { return options.selectedOptions; },
            allBindingAccessors
        );
        
        $(element).multiselect("refresh");
Reply all
Reply to author
Forward
0 new messages