Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.

Commit d16d2b6

Browse files
Robert MesserleThomasBurleson
authored andcommitted
feat(autocomplete): adds a new attribute option md-select-on-match
When enabled, an exact match in the `searchText` will automatically select a matching item if (a) there is exactly one match found and (b) the `searchText` matches the display value exactly Closes #3324. Closes #3825.
1 parent 623496e commit d16d2b6

File tree

3 files changed

+71
-23
lines changed

3 files changed

+71
-23
lines changed

src/components/autocomplete/autocomplete.spec.js

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('<md-autocomplete>', function () {
2828
}
2929

3030
describe('basic functionality', function () {
31-
it('should update selected item and search text', inject(function ($timeout, $mdConstant, $rootElement) {
31+
it('should update selected item and search text', inject(function ($timeout, $mdConstant) {
3232
var scope = createScope();
3333
var template = '\
3434
<md-autocomplete\
@@ -47,8 +47,6 @@ describe('<md-autocomplete>', function () {
4747
expect(scope.selectedItem).toBe(null);
4848

4949
element.scope().searchText = 'fo';
50-
ctrl.keydown({});
51-
element.scope().$apply();
5250
$timeout.flush();
5351

5452
expect(scope.searchText).toBe('fo');
@@ -65,7 +63,6 @@ describe('<md-autocomplete>', function () {
6563
preventDefault: angular.noop,
6664
stopPropagation: angular.noop
6765
});
68-
scope.$apply();
6966
$timeout.flush();
7067

7168
expect(scope.searchText).toBe('foo');
@@ -95,8 +92,6 @@ describe('<md-autocomplete>', function () {
9592
expect(scope.selectedItem).toBe(null);
9693

9794
element.scope().searchText = 'fo';
98-
ctrl.keydown({});
99-
element.scope().$apply();
10095
$timeout.flush();
10196

10297
expect(scope.searchText).toBe('fo');
@@ -113,7 +108,6 @@ describe('<md-autocomplete>', function () {
113108
preventDefault: angular.noop,
114109
stopPropagation: angular.noop
115110
});
116-
scope.$apply();
117111
$timeout.flush();
118112

119113
expect(scope.searchText).toBe('foo');
@@ -136,7 +130,6 @@ describe('<md-autocomplete>', function () {
136130
<span md-highlight-text="searchText">{{item.display}}</span>\
137131
</md-autocomplete>';
138132
var element = compile(template, scope);
139-
var ctrl = element.controller('mdAutocomplete');
140133
var ul = element.find('ul');
141134

142135
expect(scope.searchText).toBe('');
@@ -152,7 +145,7 @@ describe('<md-autocomplete>', function () {
152145
});
153146

154147
describe('API access', function () {
155-
it('should clear the selected item', inject(function ($timeout, $mdConstant) {
148+
it('should clear the selected item', inject(function ($timeout) {
156149
var scope = createScope();
157150
var template = '\
158151
<md-autocomplete\
@@ -167,12 +160,9 @@ describe('<md-autocomplete>', function () {
167160
var ctrl = element.controller('mdAutocomplete');
168161

169162
element.scope().searchText = 'fo';
170-
ctrl.keydown({});
171-
element.scope().$apply();
172163
$timeout.flush();
173164

174165
ctrl.select(0);
175-
element.scope().$apply();
176166
$timeout.flush();
177167

178168
expect(scope.searchText).toBe('foo');
@@ -187,10 +177,9 @@ describe('<md-autocomplete>', function () {
187177
expect(scope.selectedItem).toBe(null);
188178
}));
189179

190-
it('should notify selected item watchers', inject(function ($timeout, $mdConstant) {
191-
var scope = createScope();
192-
var scopeItemChanged = 1;
193-
scope.itemChanged = jasmine.createSpy('itemChanged');
180+
it('should notify selected item watchers', inject(function ($timeout) {
181+
var scope = createScope();
182+
scope.itemChanged = jasmine.createSpy('itemChanged');
194183

195184
var registeredWatcher = jasmine.createSpy('registeredWatcher');
196185

@@ -210,12 +199,9 @@ describe('<md-autocomplete>', function () {
210199
ctrl.registerSelectedItemWatcher(registeredWatcher);
211200

212201
element.scope().searchText = 'fo';
213-
ctrl.keydown({});
214-
element.scope().$apply();
215202
$timeout.flush();
216203

217204
ctrl.select(0);
218-
element.scope().$apply();
219205
$timeout.flush();
220206

221207
expect(scope.itemChanged).toHaveBeenCalled();
@@ -237,7 +223,7 @@ describe('<md-autocomplete>', function () {
237223
expect(scope.itemChanged.calls.mostRecent().args[ 0 ]).toBeNull();
238224
expect(scope.selectedItem).toBeNull();
239225
}));
240-
it('should pass value to item watcher', inject(function ($timeout, $mdConstant) {
226+
it('should pass value to item watcher', inject(function ($timeout) {
241227
var scope = createScope();
242228
var itemValue = null;
243229
var template = '\
@@ -257,12 +243,9 @@ describe('<md-autocomplete>', function () {
257243
var ctrl = element.controller('mdAutocomplete');
258244

259245
element.scope().searchText = 'fo';
260-
ctrl.keydown({});
261-
element.scope().$apply();
262246
$timeout.flush();
263247

264248
ctrl.select(0);
265-
element.scope().$apply();
266249
$timeout.flush();
267250

268251
expect(itemValue).not.toBeNull();
@@ -272,4 +255,51 @@ describe('<md-autocomplete>', function () {
272255
element.scope().$apply();
273256
}));
274257
});
258+
259+
describe('md-select-on-match', function () {
260+
it('should select matching item on exact match when `md-select-on-match` is toggled', inject(function ($timeout) {
261+
var scope = createScope();
262+
var template = '\
263+
<md-autocomplete\
264+
md-select-on-match\
265+
md-selected-item="selectedItem"\
266+
md-search-text="searchText"\
267+
md-items="item in match(searchText)"\
268+
md-item-text="item.display"\
269+
placeholder="placeholder">\
270+
<span md-highlight-text="searchText">{{item.display}}</span>\
271+
</md-autocomplete>';
272+
var element = compile(template, scope);
273+
274+
expect(scope.searchText).toBe('');
275+
expect(scope.selectedItem).toBe(null);
276+
277+
element.scope().searchText = 'foo';
278+
$timeout.flush();
279+
280+
expect(scope.selectedItem).not.toBe(null);
281+
expect(scope.selectedItem.display).toBe('foo');
282+
}));
283+
it('should not select matching item on exact match when `md-select-on-match` is NOT toggled', inject(function ($timeout) {
284+
var scope = createScope();
285+
var template = '\
286+
<md-autocomplete\
287+
md-selected-item="selectedItem"\
288+
md-search-text="searchText"\
289+
md-items="item in match(searchText)"\
290+
md-item-text="item.display"\
291+
placeholder="placeholder">\
292+
<span md-highlight-text="searchText">{{item.display}}</span>\
293+
</md-autocomplete>';
294+
var element = compile(template, scope);
295+
296+
expect(scope.searchText).toBe('');
297+
expect(scope.selectedItem).toBe(null);
298+
299+
element.scope().searchText = 'foo';
300+
$timeout.flush();
301+
302+
expect(scope.selectedItem).toBe(null);
303+
}));
304+
});
275305
});

src/components/autocomplete/js/autocompleteController.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
529529
if (searchText !== $scope.searchText) return; //-- just cache the results if old request
530530
ctrl.matches = matches;
531531
ctrl.hidden = shouldHide();
532+
if ($scope.selectOnMatch) selectItemOnMatch();
532533
updateMessages();
533534
positionDropdown();
534535
}
@@ -593,4 +594,18 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
593594
if (hasFocus) ctrl.hidden = shouldHide();
594595
}
595596

597+
/**
598+
* If there is only one matching item and the search text matches its display value exactly,
599+
* automatically select that item. Note: This function is only called if the user uses the
600+
* `md-select-on-match` flag.
601+
*/
602+
function selectItemOnMatch () {
603+
var searchText = $scope.searchText,
604+
matches = ctrl.matches,
605+
item = matches[ 0 ];
606+
if (matches.length === 1) getDisplayValue(item).then(function (displayValue) {
607+
if (searchText == displayValue) select(0);
608+
});
609+
}
610+
596611
}

src/components/autocomplete/js/autocompleteDirective.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ angular
5858
* FormController
5959
* @param {number=} md-input-minlength The minimum length for the input's value for validation
6060
* @param {number=} md-input-maxlength The maximum length for the input's value for validation
61+
* @param {boolean=} md-select-on-match When set, autocomplete will automatically select exact
62+
* the item if the search text is an exact match
6163
*
6264
* @usage
6365
* ###Basic Example
@@ -128,6 +130,7 @@ function MdAutocomplete () {
128130
itemText: '&mdItemText',
129131
placeholder: '@placeholder',
130132
noCache: '=?mdNoCache',
133+
selectOnMatch: '=?mdSelectOnMatch',
131134
itemChange: '&?mdSelectedItemChange',
132135
textChange: '&?mdSearchTextChange',
133136
minLength: '=?mdMinLength',

0 commit comments

Comments
 (0)