Skip to content

Instantly share code, notes, and snippets.

@Deraen
Last active August 29, 2015 13:57
Show Gist options
  • Save Deraen/9811993 to your computer and use it in GitHub Desktop.
Save Deraen/9811993 to your computer and use it in GitHub Desktop.
angular-selectize.js
/* jshint camelcase: false */
/* global $ */
// TODO: multiple items
angular.module('fooApp')
.factory('autocompleteFactory', function() {
var factory = function(overwrites) {
return {
restrict: 'E',
replace: true,
template: '<div><input/></div>',
require: 'ngModel',
scope: {
completeObj: '=',
},
link: function(scope, el, attrs, ctrl) {
var opts = _.extend({
plugins: ['restore_on_backspace'],
maxItems: 1,
createOnBlur: true,
// This currently requires version from github: Deraen/selectize.js
blurOnEmptyReturn: true,
onChange: function(val) {
// Set selectize value to ng-model (input value-attr) (this is a string, id or multiple ids separater by delimeter)
ctrl.$setViewValue(val);
// Update object. And be sure to modify existing object insted of creating a new!
if (scope.completeObj) {
if (!val) {
for (var i in scope.completeObj) {
if (scope.completeObj.hasOwnProperty(i)) scope.completeObj[i] = null;
}
} else {
_.extend(scope.completeObj, this.options[val]);
}
}
},
}, overwrites);
// Init
var $select = $('input', el).selectize(opts);
var selectize = $select[0].selectize;
// If ng-model (input el value-attr) changes
scope.$watch(function() {
return ctrl.$viewValue;
}, function(value) {
// If custom version of create function is provied, use that.
// Would be possible to use async maybe, but that might conflict with $watch('completeObj')
if (opts.createSync && !selectize.options[value]) {
selectize.addOption(opts.createSync(value));
}
// This will do nothing if option doesn't exist
selectize.setValue(value);
});
// If object is changed make sure that is used for selected item
scope.$watch('completeObj', function(obj) {
if (!obj) return;
// Create option if doesn't exist
selectize.addOption(obj);
// Update if exists
selectize.updateOption(obj[opts.valueField], obj);
// Make sure it's selected
selectize.setValue(obj[opts.valueField]);
});
}
};
};
// Following are intented to be used with my Angular Api thingie.
// It should be easy to adapt these for $http or $resource...
// or just implement load and create manually for every autocomplete type.
factory.loadFn = function(load) {
return function(q, cb) {
load({}, {q: q}).success(function(data) {
cb(data.list);
});
};
};
factory.createFn = function(parse, load) {
return function(input, cb) {
var obj = parse(input);
if (!obj) {
load({}, {q: input}).success(function(data) {
if (data.list.length === 1) cb(_.first(data.list));
else cb();
});
} else {
cb(obj);
}
};
};
return factory;
})
.directive('fooComplete', function(autocompleteFactory, Api) {
// It's not possible to use angular templates - but you should be using Lodash already...
var tpl = _.template('' +
'<div>' +
' <span class="code"><%- id %>,</span>' +
' <span class="name"><%- value %></span>' +
'</div>');
var createTpl = _.template('' +
'<div class="create">' +
' <% if(!item) { %>To create new item write: "id, value"<% } else { %>' +
' Create item: <strong><%- item.id %></strong>, <%- item.value %>.<% } %>' +
'</div>');
function parse(input) {
input = input.split(',');
return {id: input[0], value: input[1]};
}
function getFoos(params, query, reqBody) {
return $http.get('/search', {params: query});
}
// Some of options in this example are unnecessary...
return autocompleteFactory({
load: autocompleteFactory.loadFn(getFoos),
create: autocompleteFactory.createFn(parse, getFoos),
valueField: 'id',
searchField: ['id', 'value'],
render: {
option: function(item) { return tpl(item); },
item: function(item) { return tpl(item); },
option_create: function(item) { return createTpl({item: parse(item)}); },
}
});
})
// For angular-xeditable
.directive('editableFooComplete', function(editableDirectiveFactory) {
return editableDirectiveFactory({
directiveName: 'editableFooComplete',
inputTpl: '<foo-complete/>',
});
});
<foo-complete ng-model="id" complete-obj="obj"/>
<button ng-click="selectFoo()">Select something</button>
<p>ID: {{id}}</p>
<p>Obj: {{obj}}</p>
angular.module('fooApp')
.controller('fooCtrl', function($scope) {
$scope.id = '1';
$scope.obj = {id: '1', value: 'foo bar'};
$scope.$watch('obj', function(val) {
console.log('Selected obj', val);
});
$scope.selectFoo = function() {
$scope.id = '2'; // This can work if selectize.options already contains object with this id
... or
$scope.obj = {id: 2, value: 'another item'};
};
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment