var inject_binding = function (allBindings, key, value) { | |
//https://github.com/knockout/knockout/pull/932#issuecomment-26547528 | |
return { | |
has: function (bindingKey) { | |
return (bindingKey == key) || allBindings.has(bindingKey); | |
}, | |
get: function (bindingKey) { | |
var binding = allBindings.get(bindingKey); | |
if (bindingKey == key) { | |
binding = binding ? [].concat(binding, value) : value; | |
} | |
return binding; | |
} | |
}; | |
} | |
ko.bindingHandlers.selectize = { | |
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { | |
if (!allBindingsAccessor.has('optionsText')) | |
allBindingsAccessor = inject_binding(allBindingsAccessor, 'optionsText', 'name'); | |
if (!allBindingsAccessor.has('optionsValue')) | |
allBindingsAccessor = inject_binding(allBindingsAccessor, 'optionsValue', 'id'); | |
if (typeof allBindingsAccessor.get('optionsCaption') == 'undefined') | |
allBindingsAccessor = inject_binding(allBindingsAccessor, 'optionsCaption', 'Choose...'); | |
ko.bindingHandlers.options.update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext); | |
var options = { | |
valueField: allBindingsAccessor.get('optionsValue'), | |
labelField: allBindingsAccessor.get('optionsText'), | |
searchField: allBindingsAccessor.get('optionsText') | |
} | |
if (allBindingsAccessor.has('options')) { | |
var passed_options = allBindingsAccessor.get('options') | |
for (var attr_name in passed_options) { | |
options[attr_name] = passed_options[attr_name]; | |
} | |
} | |
var $select = $(element).selectize(options)[0].selectize; | |
if (typeof allBindingsAccessor.get('value') == 'function') { | |
$select.addItem(allBindingsAccessor.get('value')()); | |
allBindingsAccessor.get('value').subscribe(function (new_val) { | |
$select.addItem(new_val); | |
}) | |
} | |
if (typeof allBindingsAccessor.get('selectedOptions') == 'function') { | |
allBindingsAccessor.get('selectedOptions').subscribe(function (new_val) { | |
// Removing items which are not in new value | |
var values = $select.getValue(); | |
var items_to_remove = []; | |
for (var k in values) { | |
if (new_val.indexOf(values[k]) == -1) { | |
items_to_remove.push(values[k]); | |
} | |
} | |
for (var k in items_to_remove) { | |
$select.removeItem(items_to_remove[k]); | |
} | |
for (var k in new_val) { | |
$select.addItem(new_val[k]); | |
} | |
}); | |
var selected = allBindingsAccessor.get('selectedOptions')(); | |
for (var k in selected) { | |
$select.addItem(selected[k]); | |
} | |
} | |
if (typeof init_selectize == 'function') { | |
init_selectize($select); | |
} | |
if (typeof valueAccessor().subscribe == 'function') { | |
valueAccessor().subscribe(function (changes) { | |
// To avoid having duplicate keys, all delete operations will go first | |
var addedItems = new Array(); | |
changes.forEach(function (change) { | |
switch (change.status) { | |
case 'added': | |
addedItems.push(change.value); | |
break; | |
case 'deleted': | |
var itemId = change.value[options.valueField]; | |
if (itemId != null) $select.removeOption(itemId); | |
} | |
}); | |
addedItems.forEach(function (item) { | |
$select.addOption(item); | |
}); | |
}, null, "arrayChange"); | |
} | |
}, | |
update: function (element, valueAccessor, allBindingsAccessor) { | |
if (allBindingsAccessor.has('object')) { | |
var optionsValue = allBindingsAccessor.get('optionsValue') || 'id'; | |
var value_accessor = valueAccessor(); | |
var selected_obj = $.grep(value_accessor(), function (i) { | |
if (typeof i[optionsValue] == 'function') | |
var id = i[optionsValue] | |
else | |
var id = i[optionsValue] | |
return id == allBindingsAccessor.get('value')(); | |
})[0]; | |
if (selected_obj) { | |
allBindingsAccessor.get('object')(selected_obj); | |
} | |
} | |
} | |
} |
<h3>1. Using selectize with plain select tag, with values built in view.</h3> | |
<select data-bind="selectize: {}"> | |
<option value="1">Val 1</option> | |
<option value="2">Val 2</option> | |
</select> | |
<h3>2. Using values from observable array.</h3> | |
<select data-bind="selectize: items, value: item_id"></select> | |
<h3>3. Getting selected item as an object</h3> | |
<select data-bind="selectize: items, value: my_item_id, object: my_item"></select> | |
<h3>4. Using with multiselect</h3> | |
<select data-bind="selectize: items, selectedOptions: selected_items" multiple></select> | |
<h3>5. Passing additional options</h3> | |
<select data-bind="selectize: items, selectedOptions: selected_items2, options: {plugins: ['remove_button']}" multiple></select> |
$(document).ready(function(){ | |
ko.applyBindings(new TestVM();); | |
}); | |
function TestVM(){ | |
var self = this; | |
self.items = ko.observableArray([ | |
{'id': 1, 'name': 'One'}, | |
{'id': 2, 'name': 'Two'} | |
]); | |
self.item_id = ko.observable(); | |
self.my_item_id = ko.observable(); | |
self.my_item = ko.observable(); | |
self.selected_items = ko.observableArray([1]); | |
self.selected_items2 = ko.observableArray(); | |
} |
@stechnique
You are using options
binding and not selectize
binding?
<select data-bind="selectize: $root.Exercises, value: ExerciseID, optionsValue: function(item){return item.ExerciseID()}, optionsText: function(item){return item.ExerciseName();}, opt: {create: $root.CreateExercise}"></select>
The list does not seem to be updated when the associated observable array has items added. However, if I create a new instance of this same binding it has the updated items. I presume it's a problem with the update function, but I'm not exactly sure what needs to happen to get it working properly.
EDIT: I should note that I am simply pushing items into the observable array.
The destroy method does not revert the item properly. For example tabIndex is left at -1. See destroy method here for missing parameters:
https://github.com/brianreavis/selectize.js/blob/master/src/selectize.js
Does tagging work with this script? The first two examples here: http://brianreavis.github.io/selectize.js/
Is this binding only working in Knockout 3.0+? I'm working on an old knockout app that's stuck on 2.2.1 and this is the best option to use, but if I have to update Knockout to 3+ it may not be worth it.
I am having an issue when using webpack 3.10.0 where ko.subscribe is no longer triggered in this binding... any ideas why?
How would this work with option groups?
Hi, I love your binding, but I just noticed a problem I'm having with it in a specific use case. I have this code
This works great if the ratePlan observable is blank at first. As soon as I select something on the selectize input the model is updated. But if ratePlan was set in the beginning, I see the proper option selected in the input, but the KO model observable is updated with "undefined". So when I submit the form I lose the information. I looked at the source and could not find a reason for this. If I remove the value binding on the component then it stays with the value it had (it is not overwritten to undefined). If I replace the selectize binding with a normal options binding, it works well.
Anyone else run into this?