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"]
}
});
@ShaneCoufreur
Copy link

@DrSammyD Thanks for the response! I didn't know selectize replaced the value binding.

What I'm seeing now is that if i put a non-integer value in the optionValue property, it stops working.

http://jsfiddle.net/fm19zxj8/17/ (The new values work, because they're integers, the values in model upon creation don't, because they're string. The binding outputs the value for the strings, and something different for the int ones

@DrSammyD
Copy link
Author

@ShaneCoufreur yes, well that would be because the binding is broken 😀
Fixed
http://jsfiddle.net/fm19zxj8/23/
I think it has to do with the options binding not using the #__ko__### for a lookup of option values if the value is a string instead of an int, which doesn't make any sense to me, but what ever.

@lelelic
Copy link

lelelic commented Nov 19, 2014

Hi @DrSammyD,
i think there is an error in your last commit.

Infact, in your last fiddle you wrote this line in selectizeOptions init binding:

$.extend(allBindingsAccessor, inject_binding(allBindingsAccessor,'optionsAfterRender',function(el){
     $(el).attr('value', el[ko.bindingHandlers.options[_key].slice(1)]||el['value'])
}));

but in your last git you committed this:

$.extend(allBindingsAccessor, inject_binding(allBindingsAccessor,'optionsAfterRender',function(el){
                $el.attr('value', el[ko.bindingHandlers.options[_key].slice(1)]||el['value'])
            }));

and the options selection doesn't work.

Daniele

@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