Skip to content
This repository was archived by the owner on Feb 19, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions modules/directives/select2/select2.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* This change is so that you do not have to do an additional query yourself on top of Select2's own query
* @params [options] {object} The configuration options passed to $.fn.select2(). Refer to the documentation
*/
angular.module('ui.directives').directive('uiSelect2', ['ui.config', '$http', function (uiConfig, $http) {
angular.module('ui.directives').directive('uiSelect2', ['ui.config', '$timeout', function (uiConfig, $timeout) {
var options = {};
if (uiConfig.select2) {
angular.extend(options, uiConfig.select2);
Expand All @@ -24,7 +24,7 @@ angular.module('ui.directives').directive('uiSelect2', ['ui.config', '$http', fu
repeatOption = tElm.find('option[ng-repeat], option[data-ng-repeat]');

if (repeatOption.length) {
repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat');
repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat');
watch = jQuery.trim(repeatAttr.split('|')[0]).split(' ').pop();
}
}
Expand All @@ -47,23 +47,30 @@ angular.module('ui.directives').directive('uiSelect2', ['ui.config', '$http', fu
if (isSelect) {
elm.select2('val', controller.$modelValue);
} else {
if (isMultiple && !controller.$modelValue) {
elm.select2('data', []);
} else if (angular.isObject(controller.$modelValue)) {
elm.select2('data', controller.$modelValue);
if (isMultiple) {
if (!controller.$modelValue) {
elm.select2('data', []);
} else if (angular.isArray(controller.$modelValue)) {
elm.select2('data', controller.$modelValue);
} else {
elm.select2('val', controller.$modelValue);
}
} else {
elm.select2('val', controller.$modelValue);
if (angular.isObject(controller.$modelValue)) {
elm.select2('data', controller.$modelValue);
} else {
elm.select2('val', controller.$modelValue);
}
}
}
};


// Watch the options dataset for changes
if (watch) {
scope.$watch(watch, function (newVal, oldVal, scope) {
if (!newVal) return;
// Delayed so that the options have time to be rendered
setTimeout(function () {
$timeout(function () {
elm.select2('val', controller.$viewValue);
// Refresh angular to remove the superfluous option
elm.trigger('change');
Expand Down Expand Up @@ -105,8 +112,11 @@ angular.module('ui.directives').directive('uiSelect2', ['ui.config', '$http', fu
elm.val(scope.$eval(attrs.ngModel));

// Initialize the plugin late so that the injected DOM does not disrupt the template compiler
setTimeout(function () {
$timeout(function () {
elm.select2(opts);
// Not sure if I should just check for !isSelect OR if I should check for 'tags' key
if (!opts.initSelection && !isSelect)
controller.$setViewValue(elm.select2('data'));
});
};
}
Expand Down
152 changes: 108 additions & 44 deletions modules/directives/select2/test/select2Spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
describe('uiSelect2', function () {
'use strict';

var scope, $compile, options;
var scope, $compile, options, $timeout;
beforeEach(module('ui.directives'));
beforeEach(inject(function (_$rootScope_, _$compile_, _$window_) {
beforeEach(inject(function (_$rootScope_, _$compile_, _$window_, _$timeout_) {
scope = _$rootScope_.$new();
$compile = _$compile_;
$timeout = _$timeout_;
scope.options = {
query: function (query) {
var data = {
Expand All @@ -17,64 +18,127 @@ describe('uiSelect2', function () {
};
}));

describe('with a select element', function () {
/**
* Compile a template synchronously
* @param {String} template The string to compile
* @return {Object} A reference to the compiled template
*/
function compile(template) {
var element = $compile(template)(scope);
scope.$apply();
$timeout.flush();
return element;
}

describe('with a <select> element', function () {
describe('compiling this directive', function () {
it('should throw an error if we have no model defined', function () {
function compile() {
$compile('<select type="text" ui-reset></select>')(scope);
}
expect(compile).toThrow();
expect(function(){
compile('<select type="text" ui-reset></select>');
}).toThrow();
});
it('should proper DOM structure', function () {
scope.foo = 'bar';
scope.$digest();
var element = $compile('<select ui-select2 ng-model="foo"></select>')(scope);
// DOM is created asynchronously
//expect(element.next().is('div.select2-container')).toBe(true);
it('should create proper DOM structure', function () {
var element = compile('<select ui-select2 ng-model="foo"></select>');
expect(element.siblings().is('div.select2-container')).toBe(true);
});
});
describe('when model is changed programmatically', function(){
it('should set select2 to the value', function(){
scope.foo = 'First';
var element = compile('<select ui-select2 ng-model="foo"><option>First</option><option>Second</option></select>');
expect(element.select2('val')).toBe('First');
scope.$apply('foo = "Second"');
expect(element.select2('val')).toBe('Second');
});
it('should set select2 to the value for multiples', function(){
scope.foo = 'First';
var element = compile('<select ui-select2 multiple ng-model="foo"><option>First</option><option>Second</option><option>Third</option></select>');
expect(element.select2('val')).toEqual(['First']);
scope.$apply('foo = ["Second"]');
expect(element.select2('val')).toEqual(['Second']);
scope.$apply('foo = ["Second","Third"]');
expect(element.select2('val')).toEqual(['Second','Third']);
});
});
it('should observe the disabled attribute', function () {
var element = $compile('<select ui-select2 ng-model="foo" ng-disabled="disabled"></select>')(scope);

//expect(element.next().hasClass('select2-container-disabled')).toBe(false);
//scope.$apply(function(){
// scope.disabled = true;
//});
//expect(element.next().hasClass('select2-container-disabled')).toBe(true);
//scope.$apply(function(){
// scope.disabled = false;
//});
//expect(element.next().hasClass('select2-container-disabled')).toBe(false);
var element = compile('<select ui-select2 ng-model="foo" ng-disabled="disabled"></select>');
expect(element.siblings().hasClass('select2-container-disabled')).toBe(false);
scope.$apply('disabled = true');
expect(element.siblings().hasClass('select2-container-disabled')).toBe(true);
scope.$apply('disabled = false');
expect(element.siblings().hasClass('select2-container-disabled')).toBe(false);
});
it('should observe the multiple attribute', function () {
var element = $compile('<select ui-select2 ng-model="foo" ng-multiple="multiple"></select>')(scope);

//expect(element.next().hasClass('select2-container-multi')).toBe(false);
//scope.$apply(function(){
// scope.multiple = true;
//});
//expect(element.next().hasClass('select2-container-multi')).toBe(true);
//scope.$apply(function(){
// scope.multiple = false;
//});
//expect(element.next().hasClass('select2-container-multi')).toBe(false);
expect(element.siblings().hasClass('select2-container-multi')).toBe(false);
scope.$apply('multiple = true');
expect(element.siblings().hasClass('select2-container-multi')).toBe(true);
scope.$apply('multiple = false');
expect(element.siblings().hasClass('select2-container-multi')).toBe(false);
});
it('should observe an option with ng-repeat for changes', function(){
scope.items = ['first', 'second', 'third'];
scope.foo = 'fourth';
var element = compile('<select ui-select2 ng-model="foo"><option ng-repeat="item in items">{{item}}</option></select>');
expect(element.select2('val')).toNotBe('fourth');
scope.$apply('items=["fourth"]');
$timeout.flush();
expect(element.select2('val')).toBe('fourth');
});
});
describe('with an input element', function () {
describe('with an <input> element', function () {
describe('compiling this directive', function () {
it('should throw an error if we have no model defined', function () {
function compile() {
$compile('<input type="text" ui-reset/>')(scope);
}
expect(compile).toThrow();
expect(function() {
compile('<input ui-select2/>');
}).toThrow();
});
it('should creae proper DOM structure', function () {
var element = compile('<input ui-select2="options" ng-model="foo"/>');
expect(element.siblings().is('div.select2-container')).toBe(true);
});
it('should proper DOM structure', function () {
scope.foo = 'bar';
scope.$digest();
var element = $compile('<input ui-select2="options" ng-model="foo"/>')(scope);
// DOM is created asynchronously
// expect(element.next().is('div.select2-container')).toBe(true);
});
describe('when model is changed programmatically', function(){
describe('for single-select', function(){
it('should call select2(data, ...) for objects', function(){
var element = compile('<input ng-model="foo" ui-select2="options">');
spyOn($.fn, 'select2');
scope.$apply('foo={ id: 1, text: "first" }');
expect(element.select2).toHaveBeenCalledWith('data', { id: 1, text: "first" });
});
it('should call select2(val, ...) for strings', function(){
var element = compile('<input ng-model="foo" ui-select2="options">');
spyOn($.fn, 'select2');
scope.$apply('foo="first"');
expect(element.select2).toHaveBeenCalledWith('val', 'first');
});
});
describe('for multi-select', function(){
it('should call select2(data, ...) for arrays', function(){
var element = compile('<input ng-model="foo" multiple ui-select2="options">');
spyOn($.fn, 'select2');
scope.$apply('foo=[{ id: 1, text: "first" },{ id: 2, text: "second" }]');
expect(element.select2).toHaveBeenCalledWith('data', [{ id: 1, text: "first" },{ id: 2, text: "second" }]);
});
it('should call select2(data, []) for falsey values', function(){
var element = compile('<input ng-model="foo" multiple ui-select2="options">');
spyOn($.fn, 'select2');
scope.$apply('foo=[]');
expect(element.select2).toHaveBeenCalledWith('data', []);
});
it('should call select2(val, ...) for strings', function(){
var element = compile('<input ng-model="foo" multiple ui-select2="options">');
spyOn($.fn, 'select2');
scope.$apply('foo="first,second"');
expect(element.select2).toHaveBeenCalledWith('val', 'first,second');
});
});
});
it('should set the model when the user selects an item', function(){
var element = compile('<input ng-model="foo" multiple ui-select2="options">');
// TODO: programmactically select an option
// expect(scope.foo).toBe(/* selected val */);
});
});
});