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

Commit 74b6298

Browse files
devversionThomasBurleson
authored andcommitted
feat(autocomplete): add md-require-match validator.
Fixes #2492. Closes #8344
1 parent 2e6d141 commit 74b6298

File tree

4 files changed

+67
-1
lines changed

4 files changed

+67
-1
lines changed

src/components/autocomplete/autocomplete.spec.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,51 @@ describe('<md-autocomplete>', function() {
12481248
}));
12491249
});
12501250

1251+
describe('when requiring a match', function() {
1252+
1253+
it('should correctly update the validity', inject(function($timeout) {
1254+
var scope = createScope();
1255+
var template = '\
1256+
<form name="form">\
1257+
<md-autocomplete\
1258+
md-input-name="autocomplete"\
1259+
md-selected-item="selectedItem"\
1260+
md-search-text="searchText"\
1261+
md-items="item in match(searchText)"\
1262+
md-item-text="item.display"\
1263+
placeholder="placeholder"\
1264+
md-require-match="true">\
1265+
<span md-highlight-text="searchText">{{item.display}}</span>\
1266+
</md-autocomplete>\
1267+
</form>';
1268+
var element = compile(template, scope);
1269+
var ctrl = element.find('md-autocomplete').controller('mdAutocomplete');
1270+
1271+
element.scope().searchText = 'fo';
1272+
$timeout.flush();
1273+
1274+
ctrl.select(0);
1275+
$timeout.flush();
1276+
1277+
expect(scope.searchText).toBe('foo');
1278+
expect(scope.selectedItem).not.toBeNull();
1279+
expect(scope.selectedItem.display).toBe('foo');
1280+
expect(scope.match(scope.searchText).length).toBe(1);
1281+
1282+
expect(scope.form.autocomplete.$error['md-require-match']).toBeFalsy();
1283+
1284+
ctrl.clear();
1285+
1286+
scope.$apply();
1287+
1288+
expect(scope.searchText).toBe('');
1289+
expect(scope.selectedItem).toBe(null);
1290+
expect(scope.form.autocomplete.$error['md-require-match']).toBeTruthy();
1291+
1292+
}));
1293+
1294+
});
1295+
12511296
describe('when required', function() {
12521297
it('properly handles md-min-length="0" and undefined searchText', function() {
12531298
var scope = createScope();

src/components/autocomplete/demoFloatingLabel/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@
1616
md-search-text="ctrl.searchText"
1717
md-items="item in ctrl.querySearch(ctrl.searchText)"
1818
md-item-text="item.display"
19+
md-require-match=""
1920
md-floating-label="Favorite state">
2021
<md-item-template>
2122
<span md-highlight-text="ctrl.searchText">{{item.display}}</span>
2223
</md-item-template>
2324
<div ng-messages="searchForm.autocompleteField.$error" ng-if="searchForm.autocompleteField.$touched">
2425
<div ng-message="required">You <b>must</b> have a favorite state.</div>
26+
<div ng-message="md-require-match">Please select an existing state.</div>
2527
<div ng-message="minlength">Your entry is not long enough.</div>
2628
<div ng-message="maxlength">Your entry is too long.</div>
2729
</div>

src/components/autocomplete/js/autocompleteController.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
2020
hasFocus = false,
2121
lastCount = 0,
2222
fetchesInProgress = 0,
23-
enableWrapScroll = null;
23+
enableWrapScroll = null,
24+
inputModelCtrl = null;
2425

2526
//-- public variables with handlers
2627
defineProperty('hidden', handleHiddenChange, true);
@@ -74,6 +75,12 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
7475
});
7576
}
7677

78+
function updateModelValidators() {
79+
if (!$scope.requireMatch || !inputModelCtrl) return;
80+
81+
inputModelCtrl.$setValidity('md-require-match', !!$scope.selectedItem);
82+
}
83+
7784
/**
7885
* Calculates the dropdown's position and applies the new styles to the menu element
7986
* @returns {*}
@@ -205,9 +212,12 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
205212
wrap: $element.find('md-autocomplete-wrap')[0],
206213
root: document.body
207214
};
215+
208216
elements.li = elements.ul.getElementsByTagName('li');
209217
elements.snap = getSnapTarget();
210218
elements.$ = getAngularElements(elements);
219+
220+
inputModelCtrl = elements.$.input.controller('ngModel');
211221
}
212222

213223
/**
@@ -310,6 +320,9 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
310320
* @param previousSelectedItem
311321
*/
312322
function selectedItemChange (selectedItem, previousSelectedItem) {
323+
324+
updateModelValidators();
325+
313326
if (selectedItem) {
314327
getDisplayValue(selectedItem).then(function (val) {
315328
$scope.searchText = val;
@@ -372,9 +385,12 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
372385
*/
373386
function handleSearchText (searchText, previousSearchText) {
374387
ctrl.index = getDefaultIndex();
388+
375389
// do nothing on init
376390
if (searchText === previousSearchText) return;
377391

392+
updateModelValidators();
393+
378394
getDisplayValue($scope.selectedItem).then(function (val) {
379395
// clear selected item if search text no longer matches it
380396
if (searchText !== val) {

src/components/autocomplete/js/autocompleteDirective.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ angular
4545
* @param {string=} placeholder Placeholder text that will be forwarded to the input.
4646
* @param {boolean=} md-no-cache Disables the internal caching that happens in autocomplete
4747
* @param {boolean=} ng-disabled Determines whether or not to disable the input field
48+
* @param {boolean=} md-require-match When set to true, the autocomplete will add a validator,
49+
* which will evaluate to false, when no item is currently selected.
4850
* @param {number=} md-min-length Specifies the minimum length of text before autocomplete will
4951
* make suggestions
5052
* @param {number=} md-delay Specifies the amount of time (in milliseconds) to wait before looking
@@ -143,6 +145,7 @@ function MdAutocomplete ($$mdSvgRegistry) {
143145
itemText: '&mdItemText',
144146
placeholder: '@placeholder',
145147
noCache: '=?mdNoCache',
148+
requireMatch: '=?mdRequireMatch',
146149
selectOnMatch: '=?mdSelectOnMatch',
147150
matchInsensitive: '=?mdMatchCaseInsensitive',
148151
itemChange: '&?mdSelectedItemChange',

0 commit comments

Comments
 (0)