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

Commit

Permalink
feat(autocomplete): adds a new attribute option md-select-on-match
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Robert Messerle committed Jul 21, 2015
1 parent c5c148d commit 004b41d
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 23 deletions.
76 changes: 53 additions & 23 deletions src/components/autocomplete/autocomplete.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('<md-autocomplete>', function () {
}

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

element.scope().searchText = 'fo';
ctrl.keydown({});
element.scope().$apply();
$timeout.flush();

expect(scope.searchText).toBe('fo');
Expand All @@ -65,7 +63,6 @@ describe('<md-autocomplete>', function () {
preventDefault: angular.noop,
stopPropagation: angular.noop
});
scope.$apply();
$timeout.flush();

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

element.scope().searchText = 'fo';
ctrl.keydown({});
element.scope().$apply();
$timeout.flush();

expect(scope.searchText).toBe('fo');
Expand All @@ -113,7 +108,6 @@ describe('<md-autocomplete>', function () {
preventDefault: angular.noop,
stopPropagation: angular.noop
});
scope.$apply();
$timeout.flush();

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

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

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

element.scope().searchText = 'fo';
ctrl.keydown({});
element.scope().$apply();
$timeout.flush();

ctrl.select(0);
element.scope().$apply();
$timeout.flush();

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

it('should notify selected item watchers', inject(function ($timeout, $mdConstant) {
var scope = createScope();
var scopeItemChanged = 1;
scope.itemChanged = jasmine.createSpy('itemChanged');
it('should notify selected item watchers', inject(function ($timeout) {
var scope = createScope();
scope.itemChanged = jasmine.createSpy('itemChanged');

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

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

element.scope().searchText = 'fo';
ctrl.keydown({});
element.scope().$apply();
$timeout.flush();

ctrl.select(0);
element.scope().$apply();
$timeout.flush();

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

element.scope().searchText = 'fo';
ctrl.keydown({});
element.scope().$apply();
$timeout.flush();

ctrl.select(0);
element.scope().$apply();
$timeout.flush();

expect(itemValue).not.toBeNull();
Expand All @@ -272,4 +255,51 @@ describe('<md-autocomplete>', function () {
element.scope().$apply();
}));
});

describe('md-select-on-match', function () {
it('should select matching item on exact match when `md-select-on-match` is toggled', inject(function ($timeout) {
var scope = createScope();
var template = '\
<md-autocomplete\
md-select-on-match\
md-selected-item="selectedItem"\
md-search-text="searchText"\
md-items="item in match(searchText)"\
md-item-text="item.display"\
placeholder="placeholder">\
<span md-highlight-text="searchText">{{item.display}}</span>\
</md-autocomplete>';
var element = compile(template, scope);

expect(scope.searchText).toBe('');
expect(scope.selectedItem).toBe(null);

element.scope().searchText = 'foo';
$timeout.flush();

expect(scope.selectedItem).not.toBe(null);
expect(scope.selectedItem.display).toBe('foo');
}));
it('should not select matching item on exact match when `md-select-on-match` is NOT toggled', inject(function ($timeout) {
var scope = createScope();
var template = '\
<md-autocomplete\
md-selected-item="selectedItem"\
md-search-text="searchText"\
md-items="item in match(searchText)"\
md-item-text="item.display"\
placeholder="placeholder">\
<span md-highlight-text="searchText">{{item.display}}</span>\
</md-autocomplete>';
var element = compile(template, scope);

expect(scope.searchText).toBe('');
expect(scope.selectedItem).toBe(null);

element.scope().searchText = 'foo';
$timeout.flush();

expect(scope.selectedItem).toBe(null);
}));
});
});
15 changes: 15 additions & 0 deletions src/components/autocomplete/js/autocompleteController.js
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
if (searchText !== $scope.searchText) return; //-- just cache the results if old request
ctrl.matches = matches;
ctrl.hidden = shouldHide();
if ($scope.selectOnMatch) selectItemOnMatch();
updateMessages();
positionDropdown();
}
Expand Down Expand Up @@ -593,4 +594,18 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
if (hasFocus) ctrl.hidden = shouldHide();
}

/**
* If there is only one matching item and the search text matches its display value exactly,
* automatically select that item. Note: This function is only called if the user uses the
* `md-select-on-match` flag.
*/
function selectItemOnMatch () {
var searchText = $scope.searchText,
matches = ctrl.matches,
item = matches[ 0 ];
if (matches.length === 1) getDisplayValue(item).then(function (displayValue) {
if (searchText == displayValue) select(0);
});
}

}
1 change: 1 addition & 0 deletions src/components/autocomplete/js/autocompleteDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ function MdAutocomplete () {
itemText: '&mdItemText',
placeholder: '@placeholder',
noCache: '=?mdNoCache',
selectOnMatch: '=?mdSelectOnMatch',
itemChange: '&?mdSelectedItemChange',
textChange: '&?mdSearchTextChange',
minLength: '=?mdMinLength',
Expand Down

0 comments on commit 004b41d

Please sign in to comment.