From 704df96d471597feca324aa4e84e07532e852a73 Mon Sep 17 00:00:00 2001 From: Greg Lazarev Date: Tue, 6 May 2014 15:01:24 -0700 Subject: [PATCH 01/10] Update dist with latest from src Dist has become out of sync with src, whis is erroring: ```js ReferenceError: uiSelectMinErr is not defined ``` --- dist/select.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dist/select.js b/dist/select.js index 830bbf838..a9ce6aee4 100644 --- a/dist/select.js +++ b/dist/select.js @@ -114,7 +114,7 @@ angular.module('ui.select', []) ctrl.open = false; ctrl.disabled = undefined; // Initialized inside uiSelect directive link function ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function - ctrl.refreshDelay = undefined; // Initialized inside choices directive link function + ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function var _searchInput = $element.querySelectorAll('input.ui-select-search'); if (_searchInput.length !== 1) { @@ -174,15 +174,14 @@ angular.module('ui.select', []) ctrl.refresh = function(refreshAttr) { if (refreshAttr !== undefined) { - // Throttle / debounce - // + // Debounce // See https://github.com/angular-ui/bootstrap/blob/0.10.0/src/typeahead/typeahead.js#L155 // FYI AngularStrap typeahead does not have debouncing: https://github.com/mgcrea/angular-strap/blob/v2.0.0-rc.4/src/typeahead/typeahead.js#L177 if (_refreshDelayPromise) { $timeout.cancel(_refreshDelayPromise); } _refreshDelayPromise = $timeout(function() { - $scope.$apply(refreshAttr); + $scope.$eval(refreshAttr); }, ctrl.refreshDelay); } }; @@ -233,7 +232,6 @@ angular.module('ui.select', []) } // Bind to keyboard shortcuts - // Cannot specify a namespace: not supported by jqLite _searchInput.on('keydown', function(e) { // Keyboard shortcuts are all about the items, // does not make sense (and will crash) if ctrl.items is empty @@ -282,8 +280,8 @@ angular.module('ui.select', []) }]) .directive('uiSelect', - ['$document', 'uiSelectConfig', - function($document, uiSelectConfig) { + ['$document', 'uiSelectConfig', 'uiSelectMinErr', + function($document, uiSelectConfig, uiSelectMinErr) { return { restrict: 'EA', @@ -324,8 +322,7 @@ angular.module('ui.select', []) $select.selected = ngModel.$viewValue; }; - // See Click everywhere but here event http://stackoverflow.com/questions/12931369 - $document.on('mousedown', function(e) { + function onDocumentClick(e) { var contains = false; if (window.jQuery) { @@ -340,10 +337,13 @@ angular.module('ui.select', []) $select.close(); scope.$digest(); } - }); + } + + // See Click everywhere but here event http://stackoverflow.com/questions/12931369 + $document.on('click', onDocumentClick); scope.$on('$destroy', function() { - $document.off('mousedown'); + $document.off('click', onDocumentClick); }); // Move transcluded elements to their correct position in main template @@ -371,7 +371,7 @@ angular.module('ui.select', []) }; }]) -.directive('choices', +.directive('uiSelectChoices', ['uiSelectConfig', 'RepeatParser', 'uiSelectMinErr', function(uiSelectConfig, RepeatParser, uiSelectMinErr) { @@ -416,7 +416,7 @@ angular.module('ui.select', []) }; }]) -.directive('match', ['uiSelectConfig', function(uiSelectConfig) { +.directive('uiSelectMatch', ['uiSelectConfig', function(uiSelectConfig) { return { restrict: 'EA', require: '^uiSelect', @@ -453,10 +453,10 @@ angular.module('ui.select', []) angular.module('ui.select').run(['$templateCache', function ($templateCache) { $templateCache.put('bootstrap/choices.tpl.html', ' '); - $templateCache.put('bootstrap/match.tpl.html', ' '); + $templateCache.put('bootstrap/match.tpl.html', ' '); $templateCache.put('bootstrap/select.tpl.html', ' '); $templateCache.put('select2/choices.tpl.html', ' '); - $templateCache.put('select2/match.tpl.html', ' {{$select.placeholder}} '); + $templateCache.put('select2/match.tpl.html', ' {{$select.placeholder}} '); $templateCache.put('select2/select.tpl.html', '
'); $templateCache.put('selectize/choices.tpl.html', '
'); $templateCache.put('selectize/match.tpl.html', '
'); From c535e7e47bddac4ebfd811ac33045fac83e3c692 Mon Sep 17 00:00:00 2001 From: Andi Bade Date: Wed, 26 Mar 2014 13:45:32 +0100 Subject: [PATCH 02/10] Cherry-pick tandibar/ui-select@c398bdcbfcf09a1f5294dd5fe464fef6316edbb5 : Allow tagging --- dist/select.js | 54 +++++++++++++++++++++++++++++++++++++------------- src/select.js | 42 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/dist/select.js b/dist/select.js index a9ce6aee4..6905af922 100644 --- a/dist/select.js +++ b/dist/select.js @@ -115,6 +115,7 @@ angular.module('ui.select', []) ctrl.disabled = undefined; // Initialized inside uiSelect directive link function ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function + ctrl.allowNewValues = false; var _searchInput = $element.querySelectorAll('input.ui-select-search'); if (_searchInput.length !== 1) { @@ -188,6 +189,10 @@ angular.module('ui.select', []) // When the user clicks on an item inside the dropdown ctrl.select = function(item) { + if(ctrl.allowNewValues && !item && ctrl.search.length > 0) { + // create new item on the fly + item = ctrl.search; + } ctrl.selected = item; ctrl.close(); // Using a watch instead of $scope.ngModel.$setViewValue(item) @@ -280,7 +285,7 @@ angular.module('ui.select', []) }]) .directive('uiSelect', - ['$document', 'uiSelectConfig', 'uiSelectMinErr', + ['$document', 'uiSelectConfig', 'uiSelectMinErr', function($document, uiSelectConfig, uiSelectMinErr) { return { @@ -312,6 +317,8 @@ angular.module('ui.select', []) $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; }); + $select.allowNewValues = attrs.allowNewValues ? true : false; + scope.$watch('$select.selected', function(newValue, oldValue) { if (ngModel.$viewValue !== newValue) { ngModel.$setViewValue(newValue); @@ -322,6 +329,37 @@ angular.module('ui.select', []) $select.selected = ngModel.$viewValue; }; + function ensureHighlightVisible() { + var container = element.querySelectorAll('.ui-select-choices-content'); + var rows = container.querySelectorAll('.ui-select-choices-row'); + + var highlighted = rows[$select.activeIndex]; + if(highlighted) { + var posY = highlighted.offsetTop + highlighted.clientHeight - container[0].scrollTop; + var height = container[0].offsetHeight; + + if (posY > height) { + container[0].scrollTop += posY - height; + } else if (posY < highlighted.clientHeight) { + container[0].scrollTop -= highlighted.clientHeight - posY; + } + } + } + + // Bind to keyboard shortcuts + $select.searchInput.on('keydown', function(e) { + scope.$apply(function() { + var processed = $select.onKeydown(e.which); + if (processed) { + e.preventDefault(); + e.stopPropagation(); + + ensureHighlightVisible(); + } + }); + }); + + // See Click everywhere but here event http://stackoverflow.com/questions/12931369 function onDocumentClick(e) { var contains = false; @@ -402,7 +440,7 @@ angular.module('ui.select', []) $select.parseRepeatAttr(attrs.repeat); scope.$watch('$select.search', function() { - $select.activeIndex = 0; + $select.activeIndex = $select.allowNewValues ? -1 : 0; $select.refresh(attrs.refresh); }); @@ -450,15 +488,3 @@ angular.module('ui.select', []) return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '$&') : matchItem; }; }); - -angular.module('ui.select').run(['$templateCache', function ($templateCache) { - $templateCache.put('bootstrap/choices.tpl.html', ' '); - $templateCache.put('bootstrap/match.tpl.html', ' '); - $templateCache.put('bootstrap/select.tpl.html', ' '); - $templateCache.put('select2/choices.tpl.html', ' '); - $templateCache.put('select2/match.tpl.html', ' {{$select.placeholder}} '); - $templateCache.put('select2/select.tpl.html', '
'); - $templateCache.put('selectize/choices.tpl.html', '
'); - $templateCache.put('selectize/match.tpl.html', '
'); - $templateCache.put('selectize/select.tpl.html', '
'); -}]); \ No newline at end of file diff --git a/src/select.js b/src/select.js index 31feeea5c..6905af922 100644 --- a/src/select.js +++ b/src/select.js @@ -115,6 +115,7 @@ angular.module('ui.select', []) ctrl.disabled = undefined; // Initialized inside uiSelect directive link function ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function + ctrl.allowNewValues = false; var _searchInput = $element.querySelectorAll('input.ui-select-search'); if (_searchInput.length !== 1) { @@ -188,6 +189,10 @@ angular.module('ui.select', []) // When the user clicks on an item inside the dropdown ctrl.select = function(item) { + if(ctrl.allowNewValues && !item && ctrl.search.length > 0) { + // create new item on the fly + item = ctrl.search; + } ctrl.selected = item; ctrl.close(); // Using a watch instead of $scope.ngModel.$setViewValue(item) @@ -280,7 +285,7 @@ angular.module('ui.select', []) }]) .directive('uiSelect', - ['$document', 'uiSelectConfig', 'uiSelectMinErr', + ['$document', 'uiSelectConfig', 'uiSelectMinErr', function($document, uiSelectConfig, uiSelectMinErr) { return { @@ -312,6 +317,8 @@ angular.module('ui.select', []) $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; }); + $select.allowNewValues = attrs.allowNewValues ? true : false; + scope.$watch('$select.selected', function(newValue, oldValue) { if (ngModel.$viewValue !== newValue) { ngModel.$setViewValue(newValue); @@ -322,6 +329,37 @@ angular.module('ui.select', []) $select.selected = ngModel.$viewValue; }; + function ensureHighlightVisible() { + var container = element.querySelectorAll('.ui-select-choices-content'); + var rows = container.querySelectorAll('.ui-select-choices-row'); + + var highlighted = rows[$select.activeIndex]; + if(highlighted) { + var posY = highlighted.offsetTop + highlighted.clientHeight - container[0].scrollTop; + var height = container[0].offsetHeight; + + if (posY > height) { + container[0].scrollTop += posY - height; + } else if (posY < highlighted.clientHeight) { + container[0].scrollTop -= highlighted.clientHeight - posY; + } + } + } + + // Bind to keyboard shortcuts + $select.searchInput.on('keydown', function(e) { + scope.$apply(function() { + var processed = $select.onKeydown(e.which); + if (processed) { + e.preventDefault(); + e.stopPropagation(); + + ensureHighlightVisible(); + } + }); + }); + + // See Click everywhere but here event http://stackoverflow.com/questions/12931369 function onDocumentClick(e) { var contains = false; @@ -402,7 +440,7 @@ angular.module('ui.select', []) $select.parseRepeatAttr(attrs.repeat); scope.$watch('$select.search', function() { - $select.activeIndex = 0; + $select.activeIndex = $select.allowNewValues ? -1 : 0; $select.refresh(attrs.refresh); }); From a41caf35e74aa652f54ec6f19a20509d317f668e Mon Sep 17 00:00:00 2001 From: Andi Bade Date: Wed, 26 Mar 2014 14:16:12 +0100 Subject: [PATCH 03/10] Cherry-pick tandibar/ui-select@cebd6bbc68600fe521e78b41f0771b585336f453 : Added spec to test 'tagging' --- dist/select.js | 21 ++++----------------- src/select.js | 21 ++++----------------- test/select.spec.js | 40 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 36 deletions(-) diff --git a/dist/select.js b/dist/select.js index 6905af922..ed10af6d9 100644 --- a/dist/select.js +++ b/dist/select.js @@ -115,7 +115,7 @@ angular.module('ui.select', []) ctrl.disabled = undefined; // Initialized inside uiSelect directive link function ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function - ctrl.allowNewValues = false; + ctrl.tagging = false; var _searchInput = $element.querySelectorAll('input.ui-select-search'); if (_searchInput.length !== 1) { @@ -189,7 +189,7 @@ angular.module('ui.select', []) // When the user clicks on an item inside the dropdown ctrl.select = function(item) { - if(ctrl.allowNewValues && !item && ctrl.search.length > 0) { + if(ctrl.tagging && !item && ctrl.search.length > 0) { // create new item on the fly item = ctrl.search; } @@ -317,7 +317,7 @@ angular.module('ui.select', []) $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; }); - $select.allowNewValues = attrs.allowNewValues ? true : false; + $select.tagging = attrs.tagging ? true : false; scope.$watch('$select.selected', function(newValue, oldValue) { if (ngModel.$viewValue !== newValue) { @@ -346,19 +346,6 @@ angular.module('ui.select', []) } } - // Bind to keyboard shortcuts - $select.searchInput.on('keydown', function(e) { - scope.$apply(function() { - var processed = $select.onKeydown(e.which); - if (processed) { - e.preventDefault(); - e.stopPropagation(); - - ensureHighlightVisible(); - } - }); - }); - // See Click everywhere but here event http://stackoverflow.com/questions/12931369 function onDocumentClick(e) { var contains = false; @@ -440,7 +427,7 @@ angular.module('ui.select', []) $select.parseRepeatAttr(attrs.repeat); scope.$watch('$select.search', function() { - $select.activeIndex = $select.allowNewValues ? -1 : 0; + $select.activeIndex = $select.tagging ? -1 : 0; $select.refresh(attrs.refresh); }); diff --git a/src/select.js b/src/select.js index 6905af922..ed10af6d9 100644 --- a/src/select.js +++ b/src/select.js @@ -115,7 +115,7 @@ angular.module('ui.select', []) ctrl.disabled = undefined; // Initialized inside uiSelect directive link function ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function - ctrl.allowNewValues = false; + ctrl.tagging = false; var _searchInput = $element.querySelectorAll('input.ui-select-search'); if (_searchInput.length !== 1) { @@ -189,7 +189,7 @@ angular.module('ui.select', []) // When the user clicks on an item inside the dropdown ctrl.select = function(item) { - if(ctrl.allowNewValues && !item && ctrl.search.length > 0) { + if(ctrl.tagging && !item && ctrl.search.length > 0) { // create new item on the fly item = ctrl.search; } @@ -317,7 +317,7 @@ angular.module('ui.select', []) $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; }); - $select.allowNewValues = attrs.allowNewValues ? true : false; + $select.tagging = attrs.tagging ? true : false; scope.$watch('$select.selected', function(newValue, oldValue) { if (ngModel.$viewValue !== newValue) { @@ -346,19 +346,6 @@ angular.module('ui.select', []) } } - // Bind to keyboard shortcuts - $select.searchInput.on('keydown', function(e) { - scope.$apply(function() { - var processed = $select.onKeydown(e.which); - if (processed) { - e.preventDefault(); - e.stopPropagation(); - - ensureHighlightVisible(); - } - }); - }); - // See Click everywhere but here event http://stackoverflow.com/questions/12931369 function onDocumentClick(e) { var contains = false; @@ -440,7 +427,7 @@ angular.module('ui.select', []) $select.parseRepeatAttr(attrs.repeat); scope.$watch('$select.search', function() { - $select.activeIndex = $select.allowNewValues ? -1 : 0; + $select.activeIndex = $select.tagging ? -1 : 0; $select.refresh(attrs.refresh); }); diff --git a/test/select.spec.js b/test/select.spec.js index d6971729f..9a8d09b68 100644 --- a/test/select.spec.js +++ b/test/select.spec.js @@ -19,6 +19,17 @@ describe('ui-select tests', function() { { name: 'Nicole', email: 'nicole@email.com', age: 43 }, { name: 'Adrian', email: 'adrian@email.com', age: 21 } ]; + + scope.names = [ + 'Adam', + 'Amalie', + 'Wladimir', + 'Samantha', + 'Estefanía', + 'Natasha', + 'Nicole', + 'Adrian' + ]; })); @@ -30,15 +41,19 @@ describe('ui-select tests', function() { return el; } - function createUiSelect(attrs) { + function generateAttrsHtml(attrs) { var attrsHtml = ''; if (attrs !== undefined) { if (attrs.disabled !== undefined) { attrsHtml += ' ng-disabled="' + attrs.disabled + '"'; } if (attrs.required !== undefined) { attrsHtml += ' ng-required="' + attrs.required + '"'; } + if (attrs.tagging !== undefined) { attrsHtml += ' tagging="' + attrs.tagging + '"'; } } + return attrsHtml; + } + function createUiSelectCollection(attrs) { return compileTemplate( - ' \ + ' \ {{$select.selected.name}} \ \
\ @@ -48,6 +63,18 @@ describe('ui-select tests', function() { ); } + function createUiSelect(attrs) { + return compileTemplate( + ' \ + {{$select.selected.name}} \ + \ +
\ +
\ +
\ +
' + ); + } + function getMatchLabel(el) { return $(el).find('.ui-select-match > span[ng-transclude]:not(.ng-hide)').text(); } @@ -163,6 +190,15 @@ describe('ui-select tests', function() { expect(isDropdownOpened(el3)).toEqual(true); }); + it('should allow tagging if the attribute says so', function() { + var el = createUiSelectCollection({tagging: true}); + clickMatch(el); + + $(el).scope().$select.select("I don't exist"); + + expect($(el).scope().$select.selected).toEqual("I don't exist"); + }); + // See when an item that evaluates to false (such as "false" or "no") is selected, the placeholder is shown https://github.com/angular-ui/ui-select/pull/32 it('should not display the placeholder when item evaluates to false', function() { scope.items = ['false']; From f9a626975492dbaa9e52def86a720196b9c7b7bf Mon Sep 17 00:00:00 2001 From: Marc ALEXANDRE Date: Wed, 14 May 2014 11:04:01 +0200 Subject: [PATCH 04/10] Update dist after cherry-pick from @tandibar fork --- dist/select.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dist/select.js b/dist/select.js index ed10af6d9..d15c3e287 100644 --- a/dist/select.js +++ b/dist/select.js @@ -475,3 +475,15 @@ angular.module('ui.select', []) return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '$&') : matchItem; }; }); + +angular.module('ui.select').run(['$templateCache', function ($templateCache) { + $templateCache.put('bootstrap/choices.tpl.html', ' '); + $templateCache.put('bootstrap/match.tpl.html', ' '); + $templateCache.put('bootstrap/select.tpl.html', ' '); + $templateCache.put('select2/choices.tpl.html', '
'); + $templateCache.put('select2/match.tpl.html', ' {{$select.placeholder}} '); + $templateCache.put('select2/select.tpl.html', '
'); + $templateCache.put('selectize/choices.tpl.html', '
'); + $templateCache.put('selectize/match.tpl.html', '
'); + $templateCache.put('selectize/select.tpl.html', '
'); +}]); From c59a4b570075cae672b85605a412a54f0655312e Mon Sep 17 00:00:00 2001 From: Marc ALEXANDRE Date: Wed, 14 May 2014 11:21:22 +0200 Subject: [PATCH 05/10] Add tagging example --- examples/bootstrap.html | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/examples/bootstrap.html b/examples/bootstrap.html index 940b62a68..3ac396284 100644 --- a/examples/bootstrap.html +++ b/examples/bootstrap.html @@ -95,6 +95,21 @@ +
+ +
+ + + {{$select.selected.name}} + +
+ +
+
+ +
+
+ From 7530a8da992332dc539b9386df1d7cb663ecdaef Mon Sep 17 00:00:00 2001 From: Marc ALEXANDRE Date: Wed, 14 May 2014 11:43:52 +0200 Subject: [PATCH 06/10] Add function in attributes for tagging management --- dist/select.js | 20 ++++++++++++++------ examples/bootstrap.html | 2 +- examples/demo.js | 4 ++++ src/select.js | 20 ++++++++++++++------ 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/dist/select.js b/dist/select.js index d15c3e287..c641d325b 100644 --- a/dist/select.js +++ b/dist/select.js @@ -115,7 +115,7 @@ angular.module('ui.select', []) ctrl.disabled = undefined; // Initialized inside uiSelect directive link function ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function - ctrl.tagging = false; + ctrl.tagging = {isActivated: false, fct: undefined}; var _searchInput = $element.querySelectorAll('input.ui-select-search'); if (_searchInput.length !== 1) { @@ -189,9 +189,9 @@ angular.module('ui.select', []) // When the user clicks on an item inside the dropdown ctrl.select = function(item) { - if(ctrl.tagging && !item && ctrl.search.length > 0) { + if(ctrl.tagging.isActivated && !item && ctrl.search.length > 0) { // create new item on the fly - item = ctrl.search; + item = ctrl.tagging.fct(ctrl.search); } ctrl.selected = item; ctrl.close(); @@ -317,7 +317,16 @@ angular.module('ui.select', []) $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; }); - $select.tagging = attrs.tagging ? true : false; + attrs.$observe('tagging', function() { + if(attrs.tagging !== undefined) + { + $select.tagging = {isActivated: true, fct: scope.$eval(attrs.tagging)}; + } + else + { + $select.tagging = {isActivated: false, fct: undefined}; + } + }); scope.$watch('$select.selected', function(newValue, oldValue) { if (ngModel.$viewValue !== newValue) { @@ -346,7 +355,6 @@ angular.module('ui.select', []) } } - // See Click everywhere but here event http://stackoverflow.com/questions/12931369 function onDocumentClick(e) { var contains = false; @@ -427,7 +435,7 @@ angular.module('ui.select', []) $select.parseRepeatAttr(attrs.repeat); scope.$watch('$select.search', function() { - $select.activeIndex = $select.tagging ? -1 : 0; + $select.activeIndex = $select.tagging.isActivated ? -1 : 0; $select.refresh(attrs.refresh); }); diff --git a/examples/bootstrap.html b/examples/bootstrap.html index 3ac396284..17a40bfbc 100644 --- a/examples/bootstrap.html +++ b/examples/bootstrap.html @@ -99,7 +99,7 @@
- + {{$select.selected.name}}
diff --git a/examples/demo.js b/examples/demo.js index 46756f011..cf9f69f1d 100644 --- a/examples/demo.js +++ b/examples/demo.js @@ -56,6 +56,10 @@ app.controller('DemoCtrl', function($scope, $http) { $scope.country.selected = undefined; }; + $scope.tagging = function(name) { + return {name: name, email: name + '@gamil.com', age: 'Unknown'}; + }; + $scope.person = {}; $scope.people = [ { name: 'Adam', email: 'adam@email.com', age: 10 }, diff --git a/src/select.js b/src/select.js index ed10af6d9..afc7b3f16 100644 --- a/src/select.js +++ b/src/select.js @@ -115,7 +115,7 @@ angular.module('ui.select', []) ctrl.disabled = undefined; // Initialized inside uiSelect directive link function ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function - ctrl.tagging = false; + ctrl.tagging = {isActivated: false, fct: undefined}; var _searchInput = $element.querySelectorAll('input.ui-select-search'); if (_searchInput.length !== 1) { @@ -189,9 +189,9 @@ angular.module('ui.select', []) // When the user clicks on an item inside the dropdown ctrl.select = function(item) { - if(ctrl.tagging && !item && ctrl.search.length > 0) { + if(ctrl.tagging.isActivated && !item && ctrl.search.length > 0) { // create new item on the fly - item = ctrl.search; + item = ctrl.tagging.fct(ctrl.search); } ctrl.selected = item; ctrl.close(); @@ -317,7 +317,16 @@ angular.module('ui.select', []) $select.resetSearchInput = resetSearchInput !== undefined ? resetSearchInput : true; }); - $select.tagging = attrs.tagging ? true : false; + attrs.$observe('tagging', function() { + if(attrs.tagging !== undefined) + { + $select.tagging = {isActivated: true, fct: scope.$eval(attrs.tagging)}; + } + else + { + $select.tagging = {isActivated: false, fct: undefined}; + } + }); scope.$watch('$select.selected', function(newValue, oldValue) { if (ngModel.$viewValue !== newValue) { @@ -346,7 +355,6 @@ angular.module('ui.select', []) } } - // See Click everywhere but here event http://stackoverflow.com/questions/12931369 function onDocumentClick(e) { var contains = false; @@ -427,7 +435,7 @@ angular.module('ui.select', []) $select.parseRepeatAttr(attrs.repeat); scope.$watch('$select.search', function() { - $select.activeIndex = $select.tagging ? -1 : 0; + $select.activeIndex = $select.tagging.isActivated ? -1 : 0; $select.refresh(attrs.refresh); }); From 3ba77c99b34d4ff342d5a0e5cdf7b1e4f875dbb3 Mon Sep 17 00:00:00 2001 From: Marc ALEXANDRE Date: Wed, 14 May 2014 12:06:31 +0200 Subject: [PATCH 07/10] Fix management of key input with tagging activated --- dist/select.js | 4 ++-- src/select.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/select.js b/dist/select.js index c641d325b..e41b68a81 100644 --- a/dist/select.js +++ b/dist/select.js @@ -221,7 +221,7 @@ angular.module('ui.select', []) if (ctrl.activeIndex < ctrl.items.length - 1) { ctrl.activeIndex++; } break; case Key.Up: - if (ctrl.activeIndex > 0) { ctrl.activeIndex--; } + if (ctrl.activeIndex >= 0) { ctrl.activeIndex--; } break; case Key.Tab: case Key.Enter: @@ -240,7 +240,7 @@ angular.module('ui.select', []) _searchInput.on('keydown', function(e) { // Keyboard shortcuts are all about the items, // does not make sense (and will crash) if ctrl.items is empty - if (ctrl.items.length > 0) { + if ((ctrl.items.length > 0 && !ctrl.tagging.isActivated) || (ctrl.search.length > 0 && ctrl.tagging.isActivated)) { var key = e.which; $scope.$apply(function() { diff --git a/src/select.js b/src/select.js index afc7b3f16..32b7cbebf 100644 --- a/src/select.js +++ b/src/select.js @@ -221,7 +221,7 @@ angular.module('ui.select', []) if (ctrl.activeIndex < ctrl.items.length - 1) { ctrl.activeIndex++; } break; case Key.Up: - if (ctrl.activeIndex > 0) { ctrl.activeIndex--; } + if (ctrl.activeIndex >= 0) { ctrl.activeIndex--; } break; case Key.Tab: case Key.Enter: @@ -240,7 +240,7 @@ angular.module('ui.select', []) _searchInput.on('keydown', function(e) { // Keyboard shortcuts are all about the items, // does not make sense (and will crash) if ctrl.items is empty - if (ctrl.items.length > 0) { + if ((ctrl.items.length > 0 && !ctrl.tagging.isActivated) || (ctrl.search.length > 0 && ctrl.tagging.isActivated)) { var key = e.which; $scope.$apply(function() { From dbd78d4de54acc661a2653e90cae6548b509c350 Mon Sep 17 00:00:00 2001 From: Marc ALEXANDRE Date: Wed, 14 May 2014 12:32:17 +0200 Subject: [PATCH 08/10] Fix tests for tagging --- test/select.spec.js | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/test/select.spec.js b/test/select.spec.js index 9a8d09b68..63f7d5ecb 100644 --- a/test/select.spec.js +++ b/test/select.spec.js @@ -19,17 +19,6 @@ describe('ui-select tests', function() { { name: 'Nicole', email: 'nicole@email.com', age: 43 }, { name: 'Adrian', email: 'adrian@email.com', age: 21 } ]; - - scope.names = [ - 'Adam', - 'Amalie', - 'Wladimir', - 'Samantha', - 'Estefanía', - 'Natasha', - 'Nicole', - 'Adrian' - ]; })); @@ -41,19 +30,16 @@ describe('ui-select tests', function() { return el; } - function generateAttrsHtml(attrs) { + function createUiSelect(attrs) { var attrsHtml = ''; if (attrs !== undefined) { if (attrs.disabled !== undefined) { attrsHtml += ' ng-disabled="' + attrs.disabled + '"'; } if (attrs.required !== undefined) { attrsHtml += ' ng-required="' + attrs.required + '"'; } if (attrs.tagging !== undefined) { attrsHtml += ' tagging="' + attrs.tagging + '"'; } } - return attrsHtml; - } - function createUiSelectCollection(attrs) { return compileTemplate( - ' \ + ' \ {{$select.selected.name}} \ \
\ @@ -63,18 +49,6 @@ describe('ui-select tests', function() { ); } - function createUiSelect(attrs) { - return compileTemplate( - ' \ - {{$select.selected.name}} \ - \ -
\ -
\ -
\ -
' - ); - } - function getMatchLabel(el) { return $(el).find('.ui-select-match > span[ng-transclude]:not(.ng-hide)').text(); } @@ -191,7 +165,7 @@ describe('ui-select tests', function() { }); it('should allow tagging if the attribute says so', function() { - var el = createUiSelectCollection({tagging: true}); + var el = createUiSelect({tagging: true}); clickMatch(el); $(el).scope().$select.select("I don't exist"); From c9426c7c67e702c51e170d2c4d33dc702e9ab93d Mon Sep 17 00:00:00 2001 From: Marc ALEXANDRE Date: Wed, 14 May 2014 13:51:16 +0200 Subject: [PATCH 09/10] Clean up code and add some comments for tagging --- dist/select.js | 12 ++++++++---- src/select.js | 10 +++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/dist/select.js b/dist/select.js index e41b68a81..dffdf2ebc 100644 --- a/dist/select.js +++ b/dist/select.js @@ -191,7 +191,7 @@ angular.module('ui.select', []) ctrl.select = function(item) { if(ctrl.tagging.isActivated && !item && ctrl.search.length > 0) { // create new item on the fly - item = ctrl.tagging.fct(ctrl.search); + item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search; } ctrl.selected = item; ctrl.close(); @@ -221,7 +221,7 @@ angular.module('ui.select', []) if (ctrl.activeIndex < ctrl.items.length - 1) { ctrl.activeIndex++; } break; case Key.Up: - if (ctrl.activeIndex >= 0) { ctrl.activeIndex--; } + if (ctrl.activeIndex > 0 || (ctrl.search.length === 0 && ctrl.tagging.isActivated)) { ctrl.activeIndex--; } break; case Key.Tab: case Key.Enter: @@ -240,6 +240,8 @@ angular.module('ui.select', []) _searchInput.on('keydown', function(e) { // Keyboard shortcuts are all about the items, // does not make sense (and will crash) if ctrl.items is empty + // unless we are in tagging mode, in that case we juste need to + // have a search term if ((ctrl.items.length > 0 && !ctrl.tagging.isActivated) || (ctrl.search.length > 0 && ctrl.tagging.isActivated)) { var key = e.which; @@ -320,7 +322,9 @@ angular.module('ui.select', []) attrs.$observe('tagging', function() { if(attrs.tagging !== undefined) { - $select.tagging = {isActivated: true, fct: scope.$eval(attrs.tagging)}; + // $eval() is needed otherwise we get a string instead of a function or a boolean + var taggingEval = scope.$eval(attrs.tagging); + $select.tagging = {isActivated: true, fct: taggingEval !== true ? taggingEval : undefined}; } else { @@ -494,4 +498,4 @@ angular.module('ui.select').run(['$templateCache', function ($templateCache) { $templateCache.put('selectize/choices.tpl.html', '
'); $templateCache.put('selectize/match.tpl.html', '
'); $templateCache.put('selectize/select.tpl.html', '
'); -}]); +}]); \ No newline at end of file diff --git a/src/select.js b/src/select.js index 32b7cbebf..0de45ee99 100644 --- a/src/select.js +++ b/src/select.js @@ -191,7 +191,7 @@ angular.module('ui.select', []) ctrl.select = function(item) { if(ctrl.tagging.isActivated && !item && ctrl.search.length > 0) { // create new item on the fly - item = ctrl.tagging.fct(ctrl.search); + item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search; } ctrl.selected = item; ctrl.close(); @@ -221,7 +221,7 @@ angular.module('ui.select', []) if (ctrl.activeIndex < ctrl.items.length - 1) { ctrl.activeIndex++; } break; case Key.Up: - if (ctrl.activeIndex >= 0) { ctrl.activeIndex--; } + if (ctrl.activeIndex > 0 || (ctrl.search.length === 0 && ctrl.tagging.isActivated)) { ctrl.activeIndex--; } break; case Key.Tab: case Key.Enter: @@ -240,6 +240,8 @@ angular.module('ui.select', []) _searchInput.on('keydown', function(e) { // Keyboard shortcuts are all about the items, // does not make sense (and will crash) if ctrl.items is empty + // unless we are in tagging mode, in that case we juste need to + // have a search term if ((ctrl.items.length > 0 && !ctrl.tagging.isActivated) || (ctrl.search.length > 0 && ctrl.tagging.isActivated)) { var key = e.which; @@ -320,7 +322,9 @@ angular.module('ui.select', []) attrs.$observe('tagging', function() { if(attrs.tagging !== undefined) { - $select.tagging = {isActivated: true, fct: scope.$eval(attrs.tagging)}; + // $eval() is needed otherwise we get a string instead of a function or a boolean + var taggingEval = scope.$eval(attrs.tagging); + $select.tagging = {isActivated: true, fct: taggingEval !== true ? taggingEval : undefined}; } else { From b3bc24102a37b2880f5ee71cbd8e2a13ee4082e4 Mon Sep 17 00:00:00 2001 From: Marc ALEXANDRE Date: Thu, 15 May 2014 10:56:56 +0200 Subject: [PATCH 10/10] Add no-color option to karma tests --- Gruntfile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 1a8abdaa2..264ab80e3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -11,7 +11,8 @@ module.exports = function(grunt) { grunt.initConfig({ karma: { options: { - configFile: 'karma.conf.js' + configFile: 'karma.conf.js', + colors: grunt.option('color') }, watch: { // Does not work under Windows?