diff --git a/src/uiSelectController.js b/src/uiSelectController.js index b7dc2801c..e5bfd9ec1 100644 --- a/src/uiSelectController.js +++ b/src/uiSelectController.js @@ -369,73 +369,88 @@ uis.controller('uiSelectCtrl', // When the user selects an item with ENTER or clicks the dropdown - ctrl.select = function(item, skipFocusser, $event) { - if (item === undefined || !_isItemDisabled(item)) { - - if ( ! ctrl.items && ! ctrl.search && ! ctrl.tagging.isActivated) return; - - if (!item || !_isItemDisabled(item)) { - if(ctrl.tagging.isActivated) { - // if taggingLabel is disabled and item is undefined we pull from ctrl.search - if ( ctrl.taggingLabel === false ) { - if ( ctrl.activeIndex < 0 ) { - if (item === undefined) { - item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search; - } - if (!item || angular.equals( ctrl.items[0], item ) ) { - return; - } - } else { - // keyboard nav happened first, user selected from dropdown - item = ctrl.items[ctrl.activeIndex]; - } - } else { - // tagging always operates at index zero, taggingLabel === false pushes - // the ctrl.search value without having it injected - if ( ctrl.activeIndex === 0 ) { - // ctrl.tagging pushes items to ctrl.items, so we only have empty val - // for `item` if it is a detected duplicate - if ( item === undefined ) return; - - // create new item on the fly if we don't already have one; - // use tagging function if we have one - if ( ctrl.tagging.fct !== undefined && typeof item === 'string' ) { - item = ctrl.tagging.fct(item); - if (!item) return; - // if item type is 'string', apply the tagging label - } else if ( typeof item === 'string' ) { - // trim the trailing space - item = item.replace(ctrl.taggingLabel,'').trim(); - } - } - } - // search ctrl.selected for dupes potentially caused by tagging and return early if found - if (_isItemSelected(item)) { - ctrl.close(skipFocusser); - return; - } - } - - $scope.$broadcast('uis:select', item); + ctrl.select = function (item, skipFocusser, $event) { - var locals = {}; - locals[ctrl.parserResult.itemName] = item; + if (angular.isDefined(item) && _isItemDisabled(item)) return; - $timeout(function(){ - ctrl.onSelectCallback($scope, { - $item: item, - $model: ctrl.parserResult.modelMapper($scope, locals) - }); - }); + if (!ctrl.items && !ctrl.search && !ctrl.tagging.isActivated) return; - if (ctrl.closeOnSelect) { - ctrl.close(skipFocusser); + var dummyTaggingFunc = function(i) { return i; }; + var taggingFunc = angular.isDefined(ctrl.tagging.fct) ? ctrl.tagging.fct + : dummyTaggingFunc; + + var minActiveIndex = ctrl.taggingLabel === false ? -1 : 0; + + // The ctrl.activeIndex might not be set (eg. if the user has clicked on an entry) + // check to see if the item exists, if so use its index as the active index + var itemIndex = minActiveIndex; + if(ctrl.activeIndex === minActiveIndex) { + var idx = ctrl.items.indexOf(item); + if(idx > -1) { + itemIndex = idx; + } + } + + if(ctrl.tagging.isActivated && itemIndex === minActiveIndex) { + + if(ctrl.taggingLabel !== false) { + // If item is undefined it'll remain undefined + // and be ignore later + item = _getCleanedTag(item); + } else { + if(angular.isUndefined(item)) { + item = taggingFunc(ctrl.search); + } } - if ($event && $event.type === 'click') { - ctrl.clickTriggeredSelect = true; + + // tagging function aborted selection + // or tagged item was a duplicate + if(!item) return; + } + + // search ctrl.selected for dupes potentially caused by tagging and return early if found + if (_isItemSelected(item)) { + ctrl.close(skipFocusser); + return; + } + + function _getCleanedTag(tagItem) { + + if(angular.isString(tagItem)) { + if(taggingFunc === dummyTaggingFunc) { + // if there isn't a tagging function ensure tagging + // label is removed from value + tagItem = tagItem.replace(ctrl.taggingLabel, '').trim(); + } else { + tagItem = ctrl.tagging.fct(tagItem); } } + + return tagItem; + } + + // if we don't have an item by now something has gone wrong + if (!item) return; + + $scope.$broadcast('uis:select', item); + + var locals = {}; + locals[ctrl.parserResult.itemName] = item; + + $timeout(function(){ + ctrl.onSelectCallback($scope, { + $item: item, + $model: ctrl.parserResult.modelMapper($scope, locals) + }); + }); + + if (ctrl.closeOnSelect) { + ctrl.close(skipFocusser); } + if ($event && $event.type === 'click') { + ctrl.clickTriggeredSelect = true; + } + }; // Closes the dropdown @@ -453,14 +468,6 @@ uis.controller('uiSelectCtrl', if (!ctrl.focus) ctrl.focusInput[0].focus(); }; - ctrl.clear = function($event) { - ctrl.select(undefined); - $event.stopPropagation(); - $timeout(function() { - ctrl.focusser[0].focus(); - }, 0, false); - }; - // Toggle dropdown ctrl.toggle = function(e) { if (ctrl.open) { diff --git a/src/uiSelectSingleDirective.js b/src/uiSelectSingleDirective.js index 73abedd23..e8c4fce0c 100644 --- a/src/uiSelectSingleDirective.js +++ b/src/uiSelectSingleDirective.js @@ -64,6 +64,14 @@ uis.directive('uiSelectSingle', ['$timeout','$compile', function($timeout, $comp focusser.prop('disabled', true); //Will reactivate it on .close() }); + $select.clear = function($event) { + $select.selected = undefined; + $event.stopPropagation(); + $timeout(function() { + $select.focusser[0].focus(); + }, 0, false); + }; + //Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954 var focusser = angular.element(""); $compile(focusser)(scope); diff --git a/test/select.spec.js b/test/select.spec.js index 5b735689f..2865e0410 100644 --- a/test/select.spec.js +++ b/test/select.spec.js @@ -97,6 +97,10 @@ describe('ui-select tests', function() { }); }; + scope.simple = { + colors: ['Red', 'Green', 'Blue', 'Yellow', 'Magenta', 'Maroon', 'Umbra', 'Turquoise'], + selected: ['Red'] + }; scope.people = [ { name: 'Adam', email: 'adam@email.com', group: 'Foo', age: 12 }, @@ -620,7 +624,7 @@ describe('ui-select tests', function() { expect($(el).scope().$select.selected).not.toBeDefined(); }); - + it('should allow tagging if the attribute says so', function() { var el = createUiSelect({tagging: true}); clickMatch(el); @@ -1402,6 +1406,43 @@ describe('ui-select tests', function() { expect($(el).scope().$select.selected).toEqual('idontexist'); }); + it('should allow tagging with simple string and no label if attribute says so', function () { + + var el = compileTemplate( + ' \ + {{$select.selected}} \ + \ +
\ +
\ +
' + ); + + clickMatch(el); + + $(el).scope().$select.select("I don't exist"); + + expect($(el).scope().$select.selected).toEqual(["Red", "I don't exist"]); + expect(scope.simple.selected).toEqual(['Red', "I don't exist"]) + }); + + it('should not prevent selecting the first item when tagging with simple string and no label', function () { + + var el = compileTemplate( + ' \ + {{$item}} \ + \ +
\ +
\ +
' + ); + + // ensure test source array has not changed + expect(scope.simple.colors[1]).toBe('Green'); + clickItem(el, 'Green'); + + expect(scope.simple.selected).toEqual(['Red', 'Green']); + }); + it('should allow creating tag on ENTER in multiple select mode with tagging enabled, no labels', function() { scope.taggingFunc = function (name) {