Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

Commit

Permalink
feat(autocomplete): add md-require-match validator.
Browse files Browse the repository at this point in the history
Fixes #2492.

Closes #8344
  • Loading branch information
devversion authored and ThomasBurleson committed Jul 15, 2016
1 parent 2e6d141 commit 74b6298
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 1 deletion.
45 changes: 45 additions & 0 deletions src/components/autocomplete/autocomplete.spec.js
Expand Up @@ -1248,6 +1248,51 @@ describe('<md-autocomplete>', function() {
}));
});

describe('when requiring a match', function() {

it('should correctly update the validity', inject(function($timeout) {
var scope = createScope();
var template = '\
<form name="form">\
<md-autocomplete\
md-input-name="autocomplete"\
md-selected-item="selectedItem"\
md-search-text="searchText"\
md-items="item in match(searchText)"\
md-item-text="item.display"\
placeholder="placeholder"\
md-require-match="true">\
<span md-highlight-text="searchText">{{item.display}}</span>\
</md-autocomplete>\
</form>';
var element = compile(template, scope);
var ctrl = element.find('md-autocomplete').controller('mdAutocomplete');

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

ctrl.select(0);
$timeout.flush();

expect(scope.searchText).toBe('foo');
expect(scope.selectedItem).not.toBeNull();
expect(scope.selectedItem.display).toBe('foo');
expect(scope.match(scope.searchText).length).toBe(1);

expect(scope.form.autocomplete.$error['md-require-match']).toBeFalsy();

ctrl.clear();

scope.$apply();

expect(scope.searchText).toBe('');
expect(scope.selectedItem).toBe(null);
expect(scope.form.autocomplete.$error['md-require-match']).toBeTruthy();

}));

});

describe('when required', function() {
it('properly handles md-min-length="0" and undefined searchText', function() {
var scope = createScope();
Expand Down
2 changes: 2 additions & 0 deletions src/components/autocomplete/demoFloatingLabel/index.html
Expand Up @@ -16,12 +16,14 @@
md-search-text="ctrl.searchText"
md-items="item in ctrl.querySearch(ctrl.searchText)"
md-item-text="item.display"
md-require-match=""
md-floating-label="Favorite state">
<md-item-template>
<span md-highlight-text="ctrl.searchText">{{item.display}}</span>
</md-item-template>
<div ng-messages="searchForm.autocompleteField.$error" ng-if="searchForm.autocompleteField.$touched">
<div ng-message="required">You <b>must</b> have a favorite state.</div>
<div ng-message="md-require-match">Please select an existing state.</div>
<div ng-message="minlength">Your entry is not long enough.</div>
<div ng-message="maxlength">Your entry is too long.</div>
</div>
Expand Down
18 changes: 17 additions & 1 deletion src/components/autocomplete/js/autocompleteController.js
Expand Up @@ -20,7 +20,8 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
hasFocus = false,
lastCount = 0,
fetchesInProgress = 0,
enableWrapScroll = null;
enableWrapScroll = null,
inputModelCtrl = null;

//-- public variables with handlers
defineProperty('hidden', handleHiddenChange, true);
Expand Down Expand Up @@ -74,6 +75,12 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
});
}

function updateModelValidators() {
if (!$scope.requireMatch || !inputModelCtrl) return;

inputModelCtrl.$setValidity('md-require-match', !!$scope.selectedItem);
}

/**
* Calculates the dropdown's position and applies the new styles to the menu element
* @returns {*}
Expand Down Expand Up @@ -205,9 +212,12 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
wrap: $element.find('md-autocomplete-wrap')[0],
root: document.body
};

elements.li = elements.ul.getElementsByTagName('li');
elements.snap = getSnapTarget();
elements.$ = getAngularElements(elements);

inputModelCtrl = elements.$.input.controller('ngModel');
}

/**
Expand Down Expand Up @@ -310,6 +320,9 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
* @param previousSelectedItem
*/
function selectedItemChange (selectedItem, previousSelectedItem) {

updateModelValidators();

if (selectedItem) {
getDisplayValue(selectedItem).then(function (val) {
$scope.searchText = val;
Expand Down Expand Up @@ -372,9 +385,12 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
*/
function handleSearchText (searchText, previousSearchText) {
ctrl.index = getDefaultIndex();

// do nothing on init
if (searchText === previousSearchText) return;

updateModelValidators();

getDisplayValue($scope.selectedItem).then(function (val) {
// clear selected item if search text no longer matches it
if (searchText !== val) {
Expand Down
3 changes: 3 additions & 0 deletions src/components/autocomplete/js/autocompleteDirective.js
Expand Up @@ -45,6 +45,8 @@ angular
* @param {string=} placeholder Placeholder text that will be forwarded to the input.
* @param {boolean=} md-no-cache Disables the internal caching that happens in autocomplete
* @param {boolean=} ng-disabled Determines whether or not to disable the input field
* @param {boolean=} md-require-match When set to true, the autocomplete will add a validator,
* which will evaluate to false, when no item is currently selected.
* @param {number=} md-min-length Specifies the minimum length of text before autocomplete will
* make suggestions
* @param {number=} md-delay Specifies the amount of time (in milliseconds) to wait before looking
Expand Down Expand Up @@ -143,6 +145,7 @@ function MdAutocomplete ($$mdSvgRegistry) {
itemText: '&mdItemText',
placeholder: '@placeholder',
noCache: '=?mdNoCache',
requireMatch: '=?mdRequireMatch',
selectOnMatch: '=?mdSelectOnMatch',
matchInsensitive: '=?mdMatchCaseInsensitive',
itemChange: '&?mdSelectedItemChange',
Expand Down

0 comments on commit 74b6298

Please sign in to comment.