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

Commit 277b30c

Browse files
JasonTypesCodeswesleycho
authored andcommitted
feat(typeahead): add typeaheadSelectOnExact support
- Add support for `typeahead-select-on-exact` for exact matches Closes #3365 Fixes #3310
1 parent d0bdd2a commit 277b30c

File tree

3 files changed

+83
-5
lines changed

3 files changed

+83
-5
lines changed

src/typeahead/docs/readme.md

+9-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ The typeahead directives provide several attributes:
2727
* `typeahead-editable` <i class="glyphicon glyphicon-eye-open"></i>
2828
_(Defaults: true)_ :
2929
Should it restrict model values to the ones selected from the popup only ?
30+
31+
* `typeahead-focus-first`
32+
_(Defaults: true)_ :
33+
Should the first match automatically be focused as you type?
3034

3135
* `typeahead-input-formatter` <i class="glyphicon glyphicon-eye-open"></i>
3236
_(Defaults: undefined)_ :
@@ -39,11 +43,15 @@ The typeahead directives provide several attributes:
3943
* `typeahead-min-length` <i class="glyphicon glyphicon-eye-open"></i>
4044
_(Defaults: 1)_ :
4145
Minimal no of characters that needs to be entered before typeahead kicks-in
42-
46+
4347
* `typeahead-on-select($item, $model, $label)`
4448
_(Defaults: null)_ :
4549
A callback executed when a match is selected
4650

51+
* `typeahead-select-on-exact`
52+
_(Defaults: false)_ :
53+
Should it automatically select an item when there is one option that exactly matches the user input?
54+
4755
* `typeahead-template-url` <i class="glyphicon glyphicon-eye-open"></i>
4856
:
4957
Set custom item template
@@ -52,10 +60,6 @@ The typeahead directives provide several attributes:
5260
_(Defaults: 0)_ :
5361
Minimal wait time after last character typed before typeahead kicks-in
5462

55-
* `typeahead-focus-first`
56-
_(Defaults: true)_ :
57-
Should the first match automatically be focused as you type?
58-
5963
* `typeahead-select-on-blur`
6064
_(Defaults: false)_ :
6165
On blur, select the currently highlighted match

src/typeahead/test/typeahead.spec.js

+57
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,63 @@ describe('typeahead tests', function () {
456456
expect(inputEl.val()).toEqual('AL');
457457
});
458458
});
459+
460+
describe('select on exact match', function(){
461+
462+
it('should select on an exact match when set', function () {
463+
464+
$scope.onSelect = jasmine.createSpy('onSelect');
465+
var element = prepareInputEl('<div><input ng-model="result" typeahead-editable="false" typeahead-on-select="onSelect()" typeahead="item for item in source | filter:$viewValue" typeahead-select-on-exact="true"></div>');
466+
var inputEl = findInput(element);
467+
468+
changeInputValueTo(element, 'bar');
469+
470+
expect($scope.result).toEqual('bar');
471+
expect(inputEl.val()).toEqual('bar');
472+
expect(element).toBeClosed();
473+
expect($scope.onSelect).toHaveBeenCalled();
474+
});
475+
476+
it('should not select on an exact match by default', function () {
477+
478+
$scope.onSelect = jasmine.createSpy('onSelect');
479+
var element = prepareInputEl('<div><input ng-model="result" typeahead-editable="false" typeahead-on-select="onSelect()" typeahead="item for item in source | filter:$viewValue"></div>');
480+
var inputEl = findInput(element);
481+
482+
changeInputValueTo(element, 'bar');
483+
484+
expect($scope.result).toBeUndefined();
485+
expect(inputEl.val()).toEqual('bar');
486+
expect($scope.onSelect.calls.any()).toBe(false);
487+
});
488+
489+
it('should not be case sensitive when select on an exact match', function () {
490+
491+
$scope.onSelect = jasmine.createSpy('onSelect');
492+
var element = prepareInputEl('<div><input ng-model="result" typeahead-editable="false" typeahead-on-select="onSelect()" typeahead="item for item in source | filter:$viewValue" typeahead-select-on-exact="true"></div>');
493+
var inputEl = findInput(element);
494+
495+
changeInputValueTo(element, 'BaR');
496+
497+
expect($scope.result).toEqual('bar');
498+
expect(inputEl.val()).toEqual('bar');
499+
expect(element).toBeClosed();
500+
expect($scope.onSelect).toHaveBeenCalled();
501+
});
502+
503+
it('should not auto select when not a match with one potential result left', function () {
504+
505+
$scope.onSelect = jasmine.createSpy('onSelect');
506+
var element = prepareInputEl('<div><input ng-model="result" typeahead-editable="false" typeahead-on-select="onSelect()" typeahead="item for item in source | filter:$viewValue" typeahead-select-on-exact="true"></div>');
507+
var inputEl = findInput(element);
508+
509+
changeInputValueTo(element, 'fo');
510+
511+
expect($scope.result).toBeUndefined();
512+
expect(inputEl.val()).toEqual('fo');
513+
expect($scope.onSelect.calls.any()).toBe(false);
514+
});
515+
});
459516

460517
describe('pop-up interaction', function () {
461518
var element;

src/typeahead/typeahead.js

+17
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
6767
var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
6868

6969
var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
70+
71+
//If input matches an item of the list exactly, select it automatically
72+
var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
7073

7174
//INTERNAL VARIABLES
7275

@@ -133,6 +136,15 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
133136
element.attr('aria-activedescendant', getMatchId(index));
134137
}
135138
});
139+
140+
var inputIsExactMatch = function(inputValue, index) {
141+
142+
if (scope.matches.length > index && inputValue) {
143+
return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
144+
}
145+
146+
return false;
147+
};
136148

137149
var getMatchesAsync = function(inputValue) {
138150

@@ -166,6 +178,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
166178
recalculatePosition();
167179

168180
element.attr('aria-expanded', true);
181+
182+
//Select the single remaining option if user input matches
183+
if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
184+
scope.select(0);
185+
}
169186
} else {
170187
resetMatches();
171188
}

0 commit comments

Comments
 (0)