Skip to content
This repository was archived by the owner on Sep 5, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/components/autocomplete/autocomplete.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 @@ -141,6 +143,7 @@ function MdAutocomplete ($$mdSvgRegistry) {
itemText: '&mdItemText',
placeholder: '@placeholder',
noCache: '=?mdNoCache',
requireMatch: '=?mdRequireMatch',
selectOnMatch: '=?mdSelectOnMatch',
matchInsensitive: '=?mdMatchCaseInsensitive',
itemChange: '&?mdSelectedItemChange',
Expand Down