Skip to content

Instantly share code, notes, and snippets.

@DrSammyD
Last active July 6, 2016 13:07
Show Gist options
  • Save DrSammyD/3ae055ca1280ccd9a4ae to your computer and use it in GitHub Desktop.
Save DrSammyD/3ae055ca1280ccd9a4ae to your computer and use it in GitHub Desktop.
Selectize Binding Handler
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['knockout','jquery','selectize'], factory);
} else {
var selConstructor = (typeof selectize != "undefined")?selectize:$('<select>').selectize().data('selectize').constructor;
factory(ko,$,selConstructor);
}
})(function (ko,$,selectize) {
var _key= null;
for(_key in ko.bindingHandlers.options){
if(typeof ko.bindingHandlers.options[_key] =='string' && ko.bindingHandlers.options[_key].match('__ko__'))
break;
}
var inject_binding = function (allBindings, key, value) {
//https://github.com/knockout/knockout/pull/932#issuecomment-26547528
var has = allBindings.has;
var get = allBindings.get;
return {
has: function (bindingKey) {
return (bindingKey == key) || has.call(allBindings,bindingKey);
},
get: function (bindingKey) {
var binding = get.call(allBindings,bindingKey);
if (bindingKey == key) {
binding = binding ? [].concat(binding, value) : value;
}
return binding;
}
};
}
var setOptionNodeSelectionState =function (optionNode, isSelected) {
optionNode.selected = isSelected;
}
selectize.prototype.updateOriginalInput=function(){
var i, n, options, self = this;
if (self.$input[0].tagName.toLowerCase() === 'select') {
options = [];
ko.utils.arrayForEach(self.$input.get(0).getElementsByTagName("option"),function(node){
var isSelected = self.items.indexOf(node[ko.bindingHandlers.options[_key].slice(1)]||node['value']) !=-1
setOptionNodeSelectionState(node, isSelected);
})
} else {
self.$input.val(self.getValue());
}
self.$input.trigger('propertychange');
if (self.isSetup) {
self.trigger('change', self.$input.val());
}
}
selectize.prototype.destroy=function(){
var self = this;
var eventNS = self.eventNS;
self.trigger('destroy');
self.off();
self.$wrapper.remove();
self.$dropdown.remove();
$(window).off(eventNS);
$(document).off(eventNS);
$(document.body).off(eventNS);
delete self.$input[0].selectize;
}
ko.bindingHandlers.selectizeOptions={
init:function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var $el= $(element);
if(allBindingsAccessor.get('selectizeMultiple'))
$(element).prop('multiple',true);
$.extend(allBindingsAccessor, inject_binding(allBindingsAccessor,'optionsAfterRender',function(el){
$(el).attr('value', el[ko.bindingHandlers.options[_key].slice(1)]||el['value'])
}));
var ret= ko.bindingHandlers.options.init(element,valueAccessor, allBindingsAccessor, viewModel, bindingContext);
$el.selectize({
placeholder: ko.unwrap(allBindingsAccessor.get('optionsCaption'))
});
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$el.data('selectize').destroy();
});
return ret;
},
update:function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var ret= ko.bindingHandlers.options.update(element,valueAccessor, allBindingsAccessor, viewModel, bindingContext);
var $el= $(element);
$el.data('selectize').destroy();
var $chi=$el.children();
$el.selectize({
placeholder: ko.unwrap(allBindingsAccessor.get('optionsCaption'))
});
$el.append($chi);
ret= ko.bindingHandlers.options.update(element,valueAccessor, allBindingsAccessor, viewModel, bindingContext);
return ret;
}
}
ko.bindingHandlers.selectizeCaption = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
ko.bindingHandlers.optionsCaption.update(element,valueAccessor, allBindingsAccessor, viewModel, bindingContext);
$(element).data('selectize').settings.placeholder= ko.unwrap(valueAccessor());
$(element).data('selectize').updatePlaceholder();
}
}
ko.bindingHandlers.selectize = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
ko.bindingHandlers.value.init(element,valueAccessor, allBindingsAccessor, viewModel, bindingContext)
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
ko.unwrap(valueAccessor());
ko.bindingHandlers.value.update(element,valueAccessor, allBindingsAccessor, viewModel, bindingContext);
$el=$(element);
$el.data('selectize').setValue(
$el.find(":selected")
.map(function(i,el){
return el[ko.bindingHandlers.options[_key].slice(1)]||el['value']
}).get()[0]
);
},
after: ["selectizeOptions"]
}
ko.bindingHandlers.selectizeMultiple = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
$(element).prop('multiple',true);
ko.bindingHandlers.selectedOptions.init(element,valueAccessor, allBindingsAccessor, viewModel, bindingContext);
},
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
ko.bindingHandlers.selectedOptions.update(element,valueAccessor, allBindingsAccessor, viewModel, bindingContext);
$el=$(element);
$el.data('selectize').setValue(
$el.find(":selected")
.map(function(i,el){
return el[ko.bindingHandlers.options[_key].slice(1)]||el['value']
}).get()
);
},
after: ["selectizeOptions"]
}
});
@DrSammyD
Copy link
Author

DrSammyD commented Feb 7, 2015

@lelelic Thanks

@lelelic
Copy link

lelelic commented Apr 1, 2015

Hi DrSammyD :)
I think there is an issue with the subscribe event.. How you can see in this fiddle (http://jsfiddle.net/p3y05ukc/9/), ko-selectize is firing twice subscribe event while the "classic" option of knockout once. I've tried to investigate the code but i didn't found the codeline that generate the trouble..

Thanks,
Daniele

@ehartford
Copy link

Can I please see an example that uses selectizeMultiple? I would like to have an observable that contains the currently selected options.

@jacobmhulse
Copy link

Excellent work Dr Sammy D! This is exactly what I needed!

@ehartford: Take a look at this: http://jsfiddle.net/fm19zxj8/25/

@jacobmhulse
Copy link

@DrSammyD
I'm not sure if you are working on this still or not but I ran into an issue which I think would be easy to solve (for you).

The call to $(element).selectize({ ... });' is only made in theselectizeOptions` bindingHandler, which cannot be used if the options are hard-coded into the html. It might be a good idea to check if the element has been initialized and if not, go ahead and do so in each bindingHandler. Thoughts?

@lelelic
Copy link

lelelic commented Nov 3, 2015

Hi @DrSammyD,
I've a simple question: how can I pre-select an option if I use optionValue and OptionText?
In this fiddle I try to get it but without results: http://jsfiddle.net/bqgd81jz/1/ . If you notice the value observable is initialized with 3.

Thanks in advance.
Daniele

@krnlde
Copy link

krnlde commented Nov 28, 2015

@DrSammyD do you see a possibility to hand over selectize constructor options? For example plugins or render: {option: ...} would be very handy and it feels weird to put them in the code directly.

@lelelic
Copy link

lelelic commented Feb 16, 2016

Hey @krnlde looks at my gist https://gist.github.com/lelelic/114a286649438b6ef365. I've added the option "selectizeSettings" where you can specify all the options of selectize constructor.
For example:
<select name="sel1" class="form-control" data-bind='selectize: values, selectizeOptions: myValues, optionsText: "Description", optionsValue: "Id", selectizeSettings: {onDelete: function() { return false }}'></select>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment