Skip to content

Commit 2ed4f8e

Browse files
committed
fix(uiSelectController): Select by click on non-multiple tagging
When in tagging mode, not multiple (`taggingLabel === false`), selecting an item by click was instead calling `taggingFunc()`. This fix checks for this manual selection, whether `ctrl.search` is filled or not and acts accordingly. closes angular-ui#1357
1 parent b1c4fdc commit 2ed4f8e

File tree

2 files changed

+85
-56
lines changed

2 files changed

+85
-56
lines changed

src/uiSelectController.js

Lines changed: 61 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -294,69 +294,74 @@ uis.controller('uiSelectCtrl',
294294

295295
// When the user selects an item with ENTER or clicks the dropdown
296296
ctrl.select = function(item, skipFocusser, $event) {
297-
if (item === undefined || !item._uiSelectChoiceDisabled) {
298-
299-
if ( ! ctrl.items && ! ctrl.search && ! ctrl.tagging.isActivated) return;
300-
301-
if (!item || !item._uiSelectChoiceDisabled) {
302-
if(ctrl.tagging.isActivated) {
303-
// if taggingLabel is disabled, we pull from ctrl.search val
304-
if ( ctrl.taggingLabel === false ) {
305-
if ( ctrl.activeIndex < 0 ) {
306-
item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search;
307-
if (!item || angular.equals( ctrl.items[0], item ) ) {
308-
return;
309-
}
310-
} else {
311-
// keyboard nav happened first, user selected from dropdown
312-
item = ctrl.items[ctrl.activeIndex];
313-
}
314-
} else {
315-
// tagging always operates at index zero, taggingLabel === false pushes
316-
// the ctrl.search value without having it injected
317-
if ( ctrl.activeIndex === 0 ) {
318-
// ctrl.tagging pushes items to ctrl.items, so we only have empty val
319-
// for `item` if it is a detected duplicate
320-
if ( item === undefined ) return;
321-
322-
// create new item on the fly if we don't already have one;
323-
// use tagging function if we have one
324-
if ( ctrl.tagging.fct !== undefined && typeof item === 'string' ) {
325-
item = ctrl.tagging.fct(ctrl.search);
326-
if (!item) return;
327-
// if item type is 'string', apply the tagging label
328-
} else if ( typeof item === 'string' ) {
329-
// trim the trailing space
330-
item = item.replace(ctrl.taggingLabel,'').trim();
331-
}
332-
}
333-
}
334-
// search ctrl.selected for dupes potentially caused by tagging and return early if found
335-
if ( ctrl.selected && angular.isArray(ctrl.selected) && ctrl.selected.filter( function (selection) { return angular.equals(selection, item); }).length > 0 ) {
336-
ctrl.close(skipFocusser);
297+
if (item && item._uiSelectChoiceDisabled === true)
298+
return;
299+
300+
if (! ctrl.items && ! ctrl.search && ! ctrl.tagging.isActivated)
301+
return;
302+
303+
if (ctrl.tagging.isActivated) {
304+
//click is made on existing item, prevent from tagging, ctrl.search does not matter
305+
var manualSelection = false;
306+
if ($event && $event.type === 'click' && item) {
307+
manualSelection = true;
308+
}
309+
310+
// if taggingLabel is disabled, we pull from ctrl.search val
311+
if ( ctrl.taggingLabel === false && manualSelection === false) {
312+
if ( ctrl.activeIndex < 0 ) {
313+
item = ctrl.tagging.fct !== undefined ? ctrl.tagging.fct(ctrl.search) : ctrl.search;
314+
if (!item || angular.equals( ctrl.items[0], item ) ) {
337315
return;
338316
}
317+
} else {
318+
// keyboard nav happened first, user selected from dropdown
319+
item = ctrl.items[ctrl.activeIndex];
339320
}
321+
} else {
322+
// tagging always operates at index zero, taggingLabel === false pushes
323+
// the ctrl.search value without having it injected
324+
if ( ctrl.activeIndex === 0 ) {
325+
// ctrl.tagging pushes items to ctrl.items, so we only have empty val
326+
// for `item` if it is a detected duplicate
327+
if ( item === undefined ) return;
328+
329+
// create new item on the fly if we don't already have one;
330+
// use tagging function if we have one
331+
if ( ctrl.tagging.fct !== undefined && typeof item === 'string' ) {
332+
item = ctrl.tagging.fct(ctrl.search);
333+
if (!item) return;
334+
// if item type is 'string', apply the tagging label
335+
} else if ( typeof item === 'string' ) {
336+
// trim the trailing space
337+
item = item.replace(ctrl.taggingLabel,'').trim();
338+
}
339+
}
340+
}
341+
// search ctrl.selected for dupes potentially caused by tagging and return early if found
342+
if ( ctrl.selected && angular.isArray(ctrl.selected) && ctrl.selected.filter( function (selection) { return angular.equals(selection, item); }).length > 0 ) {
343+
ctrl.close(skipFocusser);
344+
return;
345+
}
346+
}
340347

341-
$scope.$broadcast('uis:select', item);
348+
$scope.$broadcast('uis:select', item);
342349

343-
var locals = {};
344-
locals[ctrl.parserResult.itemName] = item;
350+
var locals = {};
351+
locals[ctrl.parserResult.itemName] = item;
345352

346-
$timeout(function(){
347-
ctrl.onSelectCallback($scope, {
348-
$item: item,
349-
$model: ctrl.parserResult.modelMapper($scope, locals)
350-
});
351-
});
353+
$timeout(function(){
354+
ctrl.onSelectCallback($scope, {
355+
$item: item,
356+
$model: ctrl.parserResult.modelMapper($scope, locals)
357+
});
358+
});
352359

353-
if (ctrl.closeOnSelect) {
354-
ctrl.close(skipFocusser);
355-
}
356-
if ($event && $event.type === 'click') {
357-
ctrl.clickTriggeredSelect = true;
358-
}
359-
}
360+
if (ctrl.closeOnSelect) {
361+
ctrl.close(skipFocusser);
362+
}
363+
if ($event && $event.type === 'click') {
364+
ctrl.clickTriggeredSelect = true;
360365
}
361366
};
362367

test/select.spec.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,30 @@ describe('ui-select tests', function() {
13001300
expect($(el).scope().$select.selected).toEqual(['idontexist']);
13011301
});
13021302

1303+
it('should allow selecting an item (click) in single select mode with tagging enabled', function() {
1304+
1305+
scope.taggingFunc = function (name) {
1306+
return name;
1307+
};
1308+
1309+
var el = compileTemplate(
1310+
'<ui-select ng-model="selection.selected" tagging="taggingFunc" tagging-label="false"> \
1311+
<ui-select-match placeholder="Pick one...">{{$select.selected.name}}</ui-select-match> \
1312+
<ui-select-choices repeat="person in people | filter: $select.search"> \
1313+
<div ng-bind-html="person.name" | highlight: $select.search"></div> \
1314+
<div ng-bind-html="person.email | highlight: $select.search"></div> \
1315+
</ui-select-choices> \
1316+
</ui-select>'
1317+
);
1318+
1319+
clickMatch(el);
1320+
setSearchText(el, 'Sam');
1321+
clickItem(el, 'Samantha');
1322+
1323+
expect(scope.selection.selected).toBe(scope.people[5]);
1324+
expect(getMatchLabel(el)).toEqual('Samantha');
1325+
});
1326+
13031327
it('should append/transclude content (with correct scope) that users add at <match> tag', function () {
13041328

13051329
var el = compileTemplate(

0 commit comments

Comments
 (0)