Skip to content
This repository was archived by the owner on Oct 2, 2019. It is now read-only.

Commit 0d47821

Browse files
committed
Merge pull request #1081 from lukaw3d/fix-tagging
Fix tagging with group-by (and unit-tests)
2 parents ce6a554 + 7963684 commit 0d47821

File tree

3 files changed

+92
-10
lines changed

3 files changed

+92
-10
lines changed

examples/demo-tagging.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ <h4>(Predictive Search Model / No Labels)</h4>
8686
<p>Selected: {{multipleDemo.colors2}}</p>
8787
<hr>
8888

89-
<h3>Object Tags</h3>
89+
<h3>Object Tags (with groupBy)</h3>
9090
<ui-select multiple tagging="tagTransform" ng-model="multipleDemo.selectedPeople" theme="bootstrap" ng-disabled="disabled" style="width: 800px;" title="Choose a person">
9191
<ui-select-match placeholder="Select person...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match>
92-
<ui-select-choices repeat="person in people | propsFilter: {name: $select.search, age: $select.search}">
93-
<div ng-if="person.isTag" ng-bind-html="person.name +' <small>(new)</small>'| highlight: $select.search"></div>
92+
<ui-select-choices repeat="person in people | propsFilter: {name: $select.search, age: $select.search}" group-by="'country'">
93+
<div ng-if="person.isTag" ng-bind-html="(person.name | highlight: $select.search) +' <small>(new)</small>'"></div>
9494
<div ng-if="!person.isTag" ng-bind-html="person.name + person.isTag| highlight: $select.search"></div>
9595
<small>
9696
email: {{person.email}}
@@ -120,7 +120,7 @@ <h3>Tagging without multiple</h3>
120120
<ui-select tagging="tagTransform" ng-model="person.selected" theme="bootstrap" ng-disabled="disabled" style="width: 800px;" title="Choose a person">
121121
<ui-select-match placeholder="Select person...">{{$select.selected.name}} &lt;{{$select.selected.email}}&gt;</ui-select-match>
122122
<ui-select-choices repeat="person in people | propsFilter: {name: $select.search, age: $select.search}">
123-
<div ng-if="person.isTag" ng-bind-html="person.name +' <small>(new)</small>'| highlight: $select.search"></div>
123+
<div ng-if="person.isTag" ng-bind-html="(person.name | highlight: $select.search) +' <small>(new)</small>'"></div>
124124
<div ng-if="!person.isTag" ng-bind-html="person.name + person.isTag| highlight: $select.search"></div>
125125
<small>
126126
email: {{person.email}}

src/uiSelectMultipleDirective.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
308308
// verify the new tag doesn't match the value of a possible selection choice or an already selected item.
309309
if (
310310
stashArr.some(function (origItem) {
311-
return angular.equals(origItem, $select.tagging.fct($select.search));
311+
return angular.equals(origItem, newItem);
312312
}) ||
313313
$select.selected.some(function (origItem) {
314314
return angular.equals(origItem, newItem);
@@ -320,7 +320,7 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
320320
});
321321
return;
322322
}
323-
newItem.isTag = true;
323+
if (newItem) newItem.isTag = true;
324324
// handle newItem string and stripping dupes in tagging string context
325325
} else {
326326
// find any tagging items already in the $select.items array and store them
@@ -369,12 +369,23 @@ uis.directive('uiSelectMultiple', ['uiSelectMinErr','$timeout', function(uiSelec
369369
items = items.slice(dupeIndex+1,items.length-1);
370370
} else {
371371
items = [];
372-
items.push(newItem);
372+
if (newItem) items.push(newItem);
373373
items = items.concat(stashArr);
374374
}
375375
scope.$evalAsync( function () {
376376
$select.activeIndex = 0;
377377
$select.items = items;
378+
379+
if ($select.isGrouped) {
380+
// update item references in groups, so that indexOf will work after angular.copy
381+
var itemsWithoutTag = newItem ? items.slice(1) : items;
382+
$select.setItemsFn(itemsWithoutTag);
383+
if (newItem) {
384+
// add tag item as a new group
385+
$select.items.unshift(newItem);
386+
$select.groups.unshift({name: '', items: [newItem], tagging: true});
387+
}
388+
}
378389
});
379390
}
380391
});

test/select.spec.js

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ describe('ui-select tests', function() {
212212
scope.$digest();
213213
}
214214

215+
function showChoicesForSearch(el, search) {
216+
setSearchText(el, search);
217+
el.scope().$select.searchInput.trigger('keyup');
218+
scope.$digest();
219+
}
220+
215221

216222
// Tests
217223
//uisRepeatParser
@@ -561,7 +567,7 @@ describe('ui-select tests', function() {
561567
var el = createUiSelect({tagging: 'taggingFunc'});
562568
clickMatch(el);
563569

564-
$(el).scope().$select.search = 'idontexist';
570+
showChoicesForSearch(el, 'idontexist');
565571
$(el).scope().$select.activeIndex = 0;
566572
$(el).scope().$select.select('idontexist');
567573

@@ -1581,7 +1587,8 @@ describe('ui-select tests', function() {
15811587
describe('multi selection', function() {
15821588

15831589
function createUiSelectMultiple(attrs) {
1584-
var attrsHtml = '';
1590+
var attrsHtml = '',
1591+
choicesAttrsHtml = '';
15851592
if (attrs !== undefined) {
15861593
if (attrs.disabled !== undefined) { attrsHtml += ' ng-disabled="' + attrs.disabled + '"'; }
15871594
if (attrs.required !== undefined) { attrsHtml += ' ng-required="' + attrs.required + '"'; }
@@ -1590,12 +1597,13 @@ describe('ui-select tests', function() {
15901597
if (attrs.tagging !== undefined) { attrsHtml += ' tagging="' + attrs.tagging + '"'; }
15911598
if (attrs.taggingTokens !== undefined) { attrsHtml += ' tagging-tokens="' + attrs.taggingTokens + '"'; }
15921599
if (attrs.inputId !== undefined) { attrsHtml += ' input-id="' + attrs.inputId + '"'; }
1600+
if (attrs.groupBy !== undefined) { choicesAttrsHtml += ' group-by="' + attrs.groupBy + '"'; }
15931601
}
15941602

15951603
return compileTemplate(
15961604
'<ui-select multiple ng-model="selection.selectedMultiple"' + attrsHtml + ' theme="bootstrap" style="width: 800px;"> \
15971605
<ui-select-match placeholder="Pick one...">{{$item.name}} &lt;{{$item.email}}&gt;</ui-select-match> \
1598-
<ui-select-choices repeat="person in people | filter: $select.search"> \
1606+
<ui-select-choices repeat="person in people | filter: $select.search"' + choicesAttrsHtml + '> \
15991607
<div ng-bind-html="person.name | highlight: $select.search"></div> \
16001608
<div ng-bind-html="person.email | highlight: $select.search"></div> \
16011609
</ui-select-choices> \
@@ -2206,6 +2214,69 @@ describe('ui-select tests', function() {
22062214
expect(el.scope().$select.multiple).toBe(true);
22072215
});
22082216

2217+
it('should not call tagging function needlessly', function() {
2218+
scope.slowTaggingFunc = function (name) {
2219+
// for (var i = 0; i < 100000000; i++);
2220+
return {name: name};
2221+
};
2222+
spyOn(scope, 'slowTaggingFunc').and.callThrough();
2223+
2224+
var el = createUiSelectMultiple({tagging: 'slowTaggingFunc'});
2225+
2226+
showChoicesForSearch(el, 'Foo');
2227+
expect(el.find('.ui-select-choices-row-inner').size()).toBe(6);
2228+
2229+
showChoicesForSearch(el, 'a');
2230+
expect(el.find('.ui-select-choices-row-inner').size()).toBe(9);
2231+
2232+
expect(scope.slowTaggingFunc.calls.count()).toBe(2);
2233+
expect(scope.slowTaggingFunc.calls.count()).not.toBe(15);
2234+
});
2235+
2236+
it('should allow decline tags when tagging function returns null in multiple select mode', function() {
2237+
scope.taggingFunc = function (name) {
2238+
if (name == 'idontexist') return null;
2239+
return {
2240+
name: name,
2241+
email: name + '@email.com',
2242+
group: 'Foo',
2243+
age: 12
2244+
};
2245+
};
2246+
2247+
var el = createUiSelectMultiple({tagging: 'taggingFunc'});
2248+
2249+
showChoicesForSearch(el, 'amalie');
2250+
expect(el.find('.ui-select-choices-row-inner').size()).toBe(2);
2251+
expect(el.scope().$select.items[0]).toEqual(jasmine.objectContaining({name: 'amalie', isTag: true}));
2252+
expect(el.scope().$select.items[1]).toEqual(jasmine.objectContaining({name: 'Amalie'}));
2253+
2254+
showChoicesForSearch(el, 'idoexist');
2255+
expect(el.find('.ui-select-choices-row-inner').size()).toBe(1);
2256+
expect(el.find('.ui-select-choices-row-inner').is(':contains(idoexist@email.com)')).toBeTruthy();
2257+
2258+
showChoicesForSearch(el, 'idontexist');
2259+
expect(el.find('.ui-select-choices-row-inner').size()).toBe(0);
2260+
});
2261+
2262+
it('should allow creating tag in multi select mode with tagging and group-by enabled', function() {
2263+
scope.taggingFunc = function (name) {
2264+
return {
2265+
name: name,
2266+
email: name + '@email.com',
2267+
group: 'Foo',
2268+
age: 12
2269+
};
2270+
};
2271+
2272+
var el = createUiSelectMultiple({tagging: 'taggingFunc', groupBy: "'age'"});
2273+
2274+
showChoicesForSearch(el, 'amal');
2275+
expect(el.find('.ui-select-choices-row-inner').size()).toBe(2);
2276+
expect(el.scope().$select.items[0]).toEqual(jasmine.objectContaining({name: 'amal', email: 'amal@email.com', isTag: true}));
2277+
expect(el.scope().$select.items[1]).toEqual(jasmine.objectContaining({name: 'Amalie', email: 'amalie@email.com'}));
2278+
});
2279+
22092280
it('should allow paste tag from clipboard', function() {
22102281
scope.taggingFunc = function (name) {
22112282
return {

0 commit comments

Comments
 (0)