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

Commit 647cdd9

Browse files
Jeff Treutingwesleycho
Jeff Treuting
authored andcommitted
feat(typeahead): add noResults indicator binding
- Add ability to configure no results message Closes #2016 Closes #2792 Closes #4068
1 parent 9f73d24 commit 647cdd9

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

src/typeahead/docs/demo.html

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ <h4>Static arrays</h4>
1212

1313
<h4>Asynchronous results</h4>
1414
<pre>Model: {{asyncSelected | json}}</pre>
15-
<input type="text" ng-model="asyncSelected" placeholder="Locations loaded via $http" typeahead="address for address in getLocation($viewValue)" typeahead-loading="loadingLocations" class="form-control">
15+
<input type="text" ng-model="asyncSelected" placeholder="Locations loaded via $http" typeahead="address for address in getLocation($viewValue)" typeahead-loading="loadingLocations" typeahead-no-results="noResults" class="form-control">
1616
<i ng-show="loadingLocations" class="glyphicon glyphicon-refresh"></i>
17+
<div ng-show="noResults">
18+
<i class="glyphicon glyphicon-remove"></i> No Results Found
19+
</div>
1720

1821
<h4>Custom templates for results</h4>
1922
<pre>Model: {{customSelected | json}}</pre>

src/typeahead/docs/readme.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ The typeahead directives provide several attributes:
4343
* `typeahead-min-length` <i class="glyphicon glyphicon-eye-open"></i>
4444
_(Defaults: 1)_ :
4545
Minimal no of characters that needs to be entered before typeahead kicks-in
46-
46+
47+
* `typeahead-no-results` <i class="glyphicon glyphicon-eye-open"></i>
48+
_(Defaults: angular.noop)_ :
49+
Binding to a variable that indicates if no matching results were found
50+
4751
* `typeahead-on-select($item, $model, $label)`
4852
_(Defaults: null)_ :
4953
A callback executed when a match is selected

src/typeahead/test/typeahead.spec.js

+34
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,40 @@ describe('typeahead tests', function () {
455455
expect($scope.result).toEqual('AL');
456456
expect(inputEl.val()).toEqual('AL');
457457
});
458+
459+
it('should bind no results indicator as true when no matches returned', inject(function ($timeout) {
460+
461+
$scope.isNoResults = false;
462+
$scope.loadMatches = function (viewValue) {
463+
return $timeout(function () {
464+
return [];
465+
}, 1000);
466+
};
467+
468+
var element = prepareInputEl('<div><input ng-model="result" typeahead="item for item in loadMatches()" typeahead-no-results="isNoResults"></div>');
469+
changeInputValueTo(element, 'foo');
470+
471+
expect($scope.isNoResults).toBeFalsy();
472+
$timeout.flush();
473+
expect($scope.isNoResults).toBeTruthy();
474+
}));
475+
476+
it('should bind no results indicator as false when matches are returned', inject(function ($timeout) {
477+
478+
$scope.isNoResults = false;
479+
$scope.loadMatches = function (viewValue) {
480+
return $timeout(function () {
481+
return [viewValue];
482+
}, 1000);
483+
};
484+
485+
var element = prepareInputEl('<div><input ng-model="result" typeahead="item for item in loadMatches()" typeahead-no-results="isNoResults"></div>');
486+
changeInputValueTo(element, 'foo');
487+
488+
expect($scope.isNoResults).toBeFalsy();
489+
$timeout.flush();
490+
expect($scope.isNoResults).toBeFalsy();
491+
}));
458492
});
459493

460494
describe('select on exact match', function(){

src/typeahead/typeahead.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,15 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
6262
//should it select highlighted popup value when losing focus?
6363
var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
6464

65+
//binding to a variable that indicates if there were no results after the query is completed
66+
var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
67+
6568
var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
6669

6770
var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
6871

6972
var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
70-
73+
7174
//If input matches an item of the list exactly, select it automatically
7275
var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
7376

@@ -136,20 +139,21 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
136139
element.attr('aria-activedescendant', getMatchId(index));
137140
}
138141
});
139-
142+
140143
var inputIsExactMatch = function(inputValue, index) {
141-
144+
142145
if (scope.matches.length > index && inputValue) {
143146
return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
144147
}
145-
148+
146149
return false;
147150
};
148151

149152
var getMatchesAsync = function(inputValue) {
150153

151154
var locals = {$viewValue: inputValue};
152155
isLoadingSetter(originalScope, true);
156+
isNoResultsSetter(originalScope, false);
153157
$q.when(parserResult.source(originalScope, locals)).then(function(matches) {
154158

155159
//it might happen that several async queries were in progress if a user were typing fast
@@ -159,6 +163,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
159163
if (matches && matches.length > 0) {
160164

161165
scope.activeIdx = focusFirst ? 0 : -1;
166+
isNoResultsSetter(originalScope, false);
162167
scope.matches.length = 0;
163168

164169
//transform labels
@@ -178,13 +183,14 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
178183
recalculatePosition();
179184

180185
element.attr('aria-expanded', true);
181-
186+
182187
//Select the single remaining option if user input matches
183188
if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
184189
scope.select(0);
185190
}
186191
} else {
187192
resetMatches();
193+
isNoResultsSetter(originalScope, true);
188194
}
189195
}
190196
if (onCurrentRequest) {
@@ -193,6 +199,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap
193199
}, function(){
194200
resetMatches();
195201
isLoadingSetter(originalScope, false);
202+
isNoResultsSetter(originalScope, true);
196203
});
197204
};
198205

0 commit comments

Comments
 (0)