I'm attempting to use Bootstrap Selectpicker along with knockout.js. There is already a custom binding out there that works for the multiselect version of the selectpicker (seen here), but I need it to work with the single select version. I thought it was going to be as simple as changing the ko.observableArray
to a ko.observable
and removing the multiple
attribute -- but this doesn't seem to be the case. Any ideas on how to get this working?
Fiddle with the binding and my updated code
KO itself doesn't depend on jQuery, but you can certainly use jQuery at the same time, and indeed that's often useful if you want things like animated transitions.
KO is able to create a two-way binding if you use value to link a form element to an Observable property, so that the changes between them are exchanged among them. If you refer a simple property on ViewModel, KO will set the form element's initial state to property value.
Binding Values The binding value can be a single value, literal, a variable or can be a JavaScript expression.
EDIT See below for alternate solution
the problem is with your selectPicker.init function.
You need to call the options binding, not the value binding. the options.init sets the initial internal state, when this bypassed the options.update function will reset the value.
// regular select and observable so call the default value binding
ko.bindingHandlers.value.init(element, valueAccessor, allBindingsAccessor);
change to
// regular select and observable so call the default value binding
ko.bindingHandlers.options.init(element, valueAccessor, allBindingsAccessor);
EDIT
Ok, I took you sample back to basics to use the standard options bindings to get the normal select working.
Then use the selectpicker binding solely to initialise and refresh. It will synchronize to the select on its own.
Prior to Knockout 3, the selectPicker update function would have been invoked if any of the bindings on the element caused an update ( like updating options, value or selectedOptions ). With Knockout 3 the bindings now fire independently ( good thing ), but you now need to use subscriptions to get notified when either the options or value/selectedOptions changes.
I think you will see this is now a lot simpler and there is no distinct between single and multiple select in your custom binding. This now works if either the teamItems or itemID observable is updated.
HTML
<!-- Multiple Select -->
<select data-bind="selectedOptions: teamIDs,
options: teamItems,
optionsText: 'text',
optionsValue : 'id',
selectPicker: {}" multiple="true"></select>
JAVASCRIPT
ko.bindingHandlers.selectPicker = {
after: ['options'], /* KO 3.0 feature to ensure binding execution order */
init: function (element, valueAccessor, allBindingsAccessor) {
var $element = $(element);
$element.addClass('selectpicker').selectpicker();
var doRefresh = function() {
$element.selectpicker('refresh');
}, subscriptions = [];
// KO 3 requires subscriptions instead of relying on this binding's update
// function firing when any other binding on the element is updated.
// Add them to a subscription array so we can remove them when KO
// tears down the element. Otherwise you will have a resource leak.
var addSubscription = function(bindingKey) {
var targetObs = allBindingsAccessor.get(bindingKey);
if ( targetObs && ko.isObservable(targetObs )) {
subscriptions.push( targetObs.subscribe(doRefresh) );
}
};
addSubscription('options');
addSubscription('value'); // Single
addSubscription('selectedOptions'); // Multiple
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
while( subscriptions.length ) {
subscriptions.pop().dispose();
}
} );
},
update: function (element, valueAccessor, allBindingsAccessor) {
}
};
Knockout has excellent and robust support for computed bindings (for example item().children.length > foo().maxChildren
) and the current answers fail to support these.
So as further improvement to RobertSlanley's answer, and based on Knockout's documentation, I have created the following binding.
Knockout will handle the observable subscriptions by itself, so long as we access them in the update method. Which means we can remove the subscription cruft and just concentrate on what we really want to do:
ko.bindingHandlers.selectPicker = {
after: ['options', 'value', 'selectedOptions'],
init: function (element, valueAccessor, allBindingsAccessor) {
$(element).addClass('selectpicker').selectpicker();
},
update: function (element, valueAccessor, allBindingsAccessor) {
/* KO 3.3 will track any bindings we depend on
and call us when any of them changes */
allBindingsAccessor.get('options');
allBindingsAccessor.get('value');
allBindingsAccessor.get('selectedOptions');
$(element).selectpicker('refresh');
}
};
Here's a demo fiddle
Unfortunately I can't yet comment on above answer, but there is a little mistake in the HTML-part:
The selected values binding should be selectedOptions
and not selectOptions
.
<!-- Multiple Select -->
<select data-bind="selectedOptions: teamIDs,
options: teamItems,
optionsText: 'text',
optionsValue : 'id',
selectPicker: {}" multiple="true"></select>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With