From 72efb1f79e10bec7713e149e190044931277508b Mon Sep 17 00:00:00 2001 From: Teegrounet Date: Sat, 23 Apr 2016 11:55:27 +0200 Subject: [PATCH 1/3] Fix taggingLabel initialization for single mode Fix taggingLabel initialization for single mode When taggingLabel is not define in ui-select tag and tagging is active with string (not object), the taggingLabel is undefine and we can see " undefined" in the list. With this code, taggingLabel is initialized with '(new)' as it could be when taggingLabel tag is present whithout value. --- src/uiSelectController.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/uiSelectController.js b/src/uiSelectController.js index a82e40a74..aec603736 100644 --- a/src/uiSelectController.js +++ b/src/uiSelectController.js @@ -38,6 +38,7 @@ uis.controller('uiSelectCtrl', ctrl.multiple = undefined; // Initialized inside uiSelect directive link function ctrl.disableChoiceExpression = undefined; // Initialized inside uiSelectChoices directive link function ctrl.tagging = {isActivated: false, fct: undefined}; + ctrl.taggingLabel = '(new)'; ctrl.taggingTokens = {isActivated: false, tokens: undefined}; ctrl.lockChoiceExpression = undefined; // Initialized inside uiSelectMatch directive link function ctrl.clickTriggeredSelect = false; From e537ede5caa972267d6a4721d68aecc310f67dfc Mon Sep 17 00:00:00 2001 From: Teegrounet Date: Wed, 27 Apr 2016 22:48:59 +0200 Subject: [PATCH 2/3] perf : store tagging element perf : store tagging element store tagging element to have it all the time without comparing items[0] and filter list of items --- src/uiSelectMultipleDirective.js | 174 ++++++++----------------------- 1 file changed, 43 insertions(+), 131 deletions(-) diff --git a/src/uiSelectMultipleDirective.js b/src/uiSelectMultipleDirective.js index 6ab6c921b..e68a8095b 100644 --- a/src/uiSelectMultipleDirective.js +++ b/src/uiSelectMultipleDirective.js @@ -287,148 +287,60 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec if ($select.taggingLabel === false) return; var items = angular.copy( $select.items ); - var stashArr = angular.copy( $select.items ); var newItem; - var item; - var hasTag = false; - var dupeIndex = -1; - var tagItems; - var tagItem; - - // case for object tagging via transform `$select.tagging.fct` function - if ( $select.tagging.fct !== undefined) { - tagItems = $select.$filter('filter')(items,{'isTag': true}); - if ( tagItems.length > 0 ) { - tagItem = tagItems[0]; - } - // remove the first element, if it has the `isTag` prop we generate a new one with each keyup, shaving the previous - if ( items.length > 0 && tagItem ) { - hasTag = true; - items = items.slice(1,items.length); - stashArr = stashArr.slice(1,stashArr.length); - } - newItem = $select.tagging.fct($select.search); - // verify the new tag doesn't match the value of a possible selection choice or an already selected item. - if ( - stashArr.some(function (origItem) { - return angular.equals(origItem, newItem); - }) || - $select.selected.some(function (origItem) { - return angular.equals(origItem, newItem); - }) - ) { - scope.$evalAsync(function () { - $select.activeIndex = 0; - $select.items = items; - }); - return; - } - if (newItem) newItem.isTag = true; - // handle newItem string and stripping dupes in tagging string context - } else { - // find any tagging items already in the $select.items array and store them - tagItems = $select.$filter('filter')(items,function (item) { - return item.match($select.taggingLabel); - }); - if ( tagItems.length > 0 ) { - tagItem = tagItems[0]; - } - item = items[0]; - // remove existing tag item if found (should only ever be one tag item) - if ( item !== undefined && items.length > 0 && tagItem ) { - hasTag = true; - items = items.slice(1,items.length); - stashArr = stashArr.slice(1,stashArr.length); - } - newItem = $select.search+' '+$select.taggingLabel; - if ( _findApproxDupe($select.selected, $select.search) > -1 ) { - return; - } - // verify the the tag doesn't match the value of an existing item from - // the searched data set or the items already selected - if ( _findCaseInsensitiveDupe(stashArr.concat($select.selected)) ) { - // if there is a tag from prev iteration, strip it / queue the change - // and return early - if ( hasTag ) { - items = stashArr; - scope.$evalAsync( function () { - $select.activeIndex = 0; - $select.items = items; - }); - } - return; - } - if ( _findCaseInsensitiveDupe(stashArr) ) { - // if there is a tag from prev iteration, strip it - if ( hasTag ) { - $select.items = stashArr.slice(1,stashArr.length); - } - return; - } + + // remove the first element, if it has the `isTag` prop we generate a new one with each keyup, shaving the previous + if ( ctrl.taggingElement.isActive===true && items.length > 0 && angular.equals(items[0], ctrl.taggingElement.withTag) ) { + items = items.slice(1,items.length); } - if ( hasTag ) dupeIndex = _findApproxDupe($select.selected, newItem); - // dupe found, shave the first item - if ( dupeIndex > -1 ) { - items = items.slice(dupeIndex+1,items.length-1); - } else { - items = []; - if (newItem) items.push(newItem); - items = items.concat(stashArr); + + var isTaggingObject = ctrl.tagging.fct !== undefined; + newItem = isTaggingObject ? ctrl.tagging.fct(ctrl.search) : ctrl.search; + + var functionToCompare = isTaggingObject ? ctrl.tagging.equals : _compareCaseInsensitive; + + if ( + !ctrl.search + || + items.some(function (origItem) { + return functionToCompare(origItem, newItem); + }) + || + ctrl.multiple && ctrl.selected.some(function (origItem) { + return functionToCompare(origItem, newItem); + }) + ) { + scope.$evalAsync(function () { + $select.activeIndex = 0; + ctrl.taggingElement = {isActive: false}; + $select.items = items; + }); } + scope.$evalAsync( function () { - $select.activeIndex = 0; - $select.items = items; - - if ($select.isGrouped) { - // update item references in groups, so that indexOf will work after angular.copy - var itemsWithoutTag = newItem ? items.slice(1) : items; - $select.setItemsFn(itemsWithoutTag); - if (newItem) { - // add tag item as a new group - $select.items.unshift(newItem); - $select.groups.unshift({name: '', items: [newItem], tagging: true}); + if (newItem) { + ctrl.taggingElement.withoutTag = newItem; + if (isTaggingObject) { + newItem.isTag = true; + } else { + newItem = ctrl.search + ' ' + ctrl.taggingLabel; } + ctrl.taggingElement.withTag = newItem; + + items.unshift(newItem); } + + $select.activeIndex = 0; + ctrl.taggingElement.isActive = true; + $select.setItemsFn(items); }); } }); - function _findCaseInsensitiveDupe(arr) { - if ( arr === undefined || $select.search === undefined ) { + function _compareCaseInsensitive(val1, val2) { + if ( val1 === undefined || val2 === undefined ) { return false; } - var hasDupe = arr.filter( function (origItem) { - if ( $select.search.toUpperCase() === undefined || origItem === undefined ) { - return false; - } - return origItem.toUpperCase() === $select.search.toUpperCase(); - }).length > 0; - - return hasDupe; - } - function _findApproxDupe(haystack, needle) { - var dupeIndex = -1; - if(angular.isArray(haystack)) { - var tempArr = angular.copy(haystack); - for (var i = 0; i Date: Wed, 27 Apr 2016 23:30:58 +0200 Subject: [PATCH 3/3] Revert "perf : store tagging element" This reverts commit e537ede5caa972267d6a4721d68aecc310f67dfc. --- src/uiSelectMultipleDirective.js | 174 +++++++++++++++++++++++-------- 1 file changed, 131 insertions(+), 43 deletions(-) diff --git a/src/uiSelectMultipleDirective.js b/src/uiSelectMultipleDirective.js index e68a8095b..6ab6c921b 100644 --- a/src/uiSelectMultipleDirective.js +++ b/src/uiSelectMultipleDirective.js @@ -287,60 +287,148 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec if ($select.taggingLabel === false) return; var items = angular.copy( $select.items ); + var stashArr = angular.copy( $select.items ); var newItem; - - // remove the first element, if it has the `isTag` prop we generate a new one with each keyup, shaving the previous - if ( ctrl.taggingElement.isActive===true && items.length > 0 && angular.equals(items[0], ctrl.taggingElement.withTag) ) { - items = items.slice(1,items.length); - } - - var isTaggingObject = ctrl.tagging.fct !== undefined; - newItem = isTaggingObject ? ctrl.tagging.fct(ctrl.search) : ctrl.search; - - var functionToCompare = isTaggingObject ? ctrl.tagging.equals : _compareCaseInsensitive; - - if ( - !ctrl.search - || - items.some(function (origItem) { - return functionToCompare(origItem, newItem); - }) - || - ctrl.multiple && ctrl.selected.some(function (origItem) { - return functionToCompare(origItem, newItem); - }) - ) { - scope.$evalAsync(function () { - $select.activeIndex = 0; - ctrl.taggingElement = {isActive: false}; - $select.items = items; + var item; + var hasTag = false; + var dupeIndex = -1; + var tagItems; + var tagItem; + + // case for object tagging via transform `$select.tagging.fct` function + if ( $select.tagging.fct !== undefined) { + tagItems = $select.$filter('filter')(items,{'isTag': true}); + if ( tagItems.length > 0 ) { + tagItem = tagItems[0]; + } + // remove the first element, if it has the `isTag` prop we generate a new one with each keyup, shaving the previous + if ( items.length > 0 && tagItem ) { + hasTag = true; + items = items.slice(1,items.length); + stashArr = stashArr.slice(1,stashArr.length); + } + newItem = $select.tagging.fct($select.search); + // verify the new tag doesn't match the value of a possible selection choice or an already selected item. + if ( + stashArr.some(function (origItem) { + return angular.equals(origItem, newItem); + }) || + $select.selected.some(function (origItem) { + return angular.equals(origItem, newItem); + }) + ) { + scope.$evalAsync(function () { + $select.activeIndex = 0; + $select.items = items; + }); + return; + } + if (newItem) newItem.isTag = true; + // handle newItem string and stripping dupes in tagging string context + } else { + // find any tagging items already in the $select.items array and store them + tagItems = $select.$filter('filter')(items,function (item) { + return item.match($select.taggingLabel); }); + if ( tagItems.length > 0 ) { + tagItem = tagItems[0]; + } + item = items[0]; + // remove existing tag item if found (should only ever be one tag item) + if ( item !== undefined && items.length > 0 && tagItem ) { + hasTag = true; + items = items.slice(1,items.length); + stashArr = stashArr.slice(1,stashArr.length); + } + newItem = $select.search+' '+$select.taggingLabel; + if ( _findApproxDupe($select.selected, $select.search) > -1 ) { + return; + } + // verify the the tag doesn't match the value of an existing item from + // the searched data set or the items already selected + if ( _findCaseInsensitiveDupe(stashArr.concat($select.selected)) ) { + // if there is a tag from prev iteration, strip it / queue the change + // and return early + if ( hasTag ) { + items = stashArr; + scope.$evalAsync( function () { + $select.activeIndex = 0; + $select.items = items; + }); + } + return; + } + if ( _findCaseInsensitiveDupe(stashArr) ) { + // if there is a tag from prev iteration, strip it + if ( hasTag ) { + $select.items = stashArr.slice(1,stashArr.length); + } + return; + } + } + if ( hasTag ) dupeIndex = _findApproxDupe($select.selected, newItem); + // dupe found, shave the first item + if ( dupeIndex > -1 ) { + items = items.slice(dupeIndex+1,items.length-1); + } else { + items = []; + if (newItem) items.push(newItem); + items = items.concat(stashArr); } - scope.$evalAsync( function () { - if (newItem) { - ctrl.taggingElement.withoutTag = newItem; - if (isTaggingObject) { - newItem.isTag = true; - } else { - newItem = ctrl.search + ' ' + ctrl.taggingLabel; + $select.activeIndex = 0; + $select.items = items; + + if ($select.isGrouped) { + // update item references in groups, so that indexOf will work after angular.copy + var itemsWithoutTag = newItem ? items.slice(1) : items; + $select.setItemsFn(itemsWithoutTag); + if (newItem) { + // add tag item as a new group + $select.items.unshift(newItem); + $select.groups.unshift({name: '', items: [newItem], tagging: true}); } - ctrl.taggingElement.withTag = newItem; - - items.unshift(newItem); } - - $select.activeIndex = 0; - ctrl.taggingElement.isActive = true; - $select.setItemsFn(items); }); } }); - function _compareCaseInsensitive(val1, val2) { - if ( val1 === undefined || val2 === undefined ) { + function _findCaseInsensitiveDupe(arr) { + if ( arr === undefined || $select.search === undefined ) { return false; } - return val1.toLowerCase() === val2.toLowerCase(); + var hasDupe = arr.filter( function (origItem) { + if ( $select.search.toUpperCase() === undefined || origItem === undefined ) { + return false; + } + return origItem.toUpperCase() === $select.search.toUpperCase(); + }).length > 0; + + return hasDupe; + } + function _findApproxDupe(haystack, needle) { + var dupeIndex = -1; + if(angular.isArray(haystack)) { + var tempArr = angular.copy(haystack); + for (var i = 0; i