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

Commit a3755d0

Browse files
devversionjelbourn
authored andcommitted
feat(autocomplete): forward ngBlur and ngFocus attributes (#9233)
Developers are currently not able to use ngBlur or ngFocus on the autocomplete, since the autocomplete is handling its focus from an underlaying input. References #4492
1 parent 215fae4 commit a3755d0

File tree

3 files changed

+89
-7
lines changed

3 files changed

+89
-7
lines changed

src/components/autocomplete/autocomplete.spec.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,66 @@ describe('<md-autocomplete>', function() {
386386
element.remove();
387387
}));
388388

389+
it('should emit the ngBlur event from the input', inject(function() {
390+
var scope = createScope(null, {
391+
onBlur: jasmine.createSpy('onBlur event')
392+
});
393+
394+
var template =
395+
'<md-autocomplete ' +
396+
'md-selected-item="selectedItem" ' +
397+
'md-search-text="searchText" ' +
398+
'md-items="item in match(searchText)" ' +
399+
'md-item-text="item.display" ' +
400+
'ng-blur="onBlur($event)" ' +
401+
'placeholder="placeholder">' +
402+
'<span md-highlight-text="searchText">{{item.display}}</span>' +
403+
'</md-autocomplete>';
404+
405+
var element = compile(template, scope);
406+
var input = element.find('input');
407+
408+
input.triggerHandler('blur');
409+
410+
expect(scope.onBlur).toHaveBeenCalledTimes(1);
411+
412+
// Confirm that the ngFocus event was called with the $event local.
413+
var focusEvent = scope.onBlur.calls.mostRecent().args[0];
414+
expect(focusEvent.target).toBe(input[0]);
415+
416+
element.remove();
417+
}));
418+
419+
it('should emit the ngFocus event from the input', inject(function() {
420+
var scope = createScope(null, {
421+
onFocus: jasmine.createSpy('onFocus event')
422+
});
423+
424+
var template =
425+
'<md-autocomplete ' +
426+
'md-selected-item="selectedItem" ' +
427+
'md-search-text="searchText" ' +
428+
'md-items="item in match(searchText)" ' +
429+
'md-item-text="item.display" ' +
430+
'ng-focus="onFocus($event)" ' +
431+
'placeholder="placeholder">' +
432+
'<span md-highlight-text="searchText">{{item.display}}</span>' +
433+
'</md-autocomplete>';
434+
435+
var element = compile(template, scope);
436+
var input = element.find('input');
437+
438+
input.triggerHandler('focus');
439+
440+
expect(scope.onFocus).toHaveBeenCalledTimes(1);
441+
442+
// Confirm that the ngFocus event was called with the $event object.
443+
var focusEvent = scope.onFocus.calls.mostRecent().args[0];
444+
expect(focusEvent.target).toBe(input[0]);
445+
446+
element.remove();
447+
}));
448+
389449
it('should not show a loading progress when the items object is invalid', inject(function() {
390450
var scope = createScope(null, {
391451
match: function() {

src/components/autocomplete/js/autocompleteController.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,12 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
426426
/**
427427
* Handles input blur event, determines if the dropdown should hide.
428428
*/
429-
function blur () {
429+
function blur($event) {
430430
hasFocus = false;
431+
431432
if (!noBlur) {
432433
ctrl.hidden = shouldHide();
434+
evalAttr('ngBlur', { $event: $event });
433435
}
434436
}
435437

@@ -450,10 +452,19 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
450452
*/
451453
function focus($event) {
452454
hasFocus = true;
453-
//-- if searchText is null, let's force it to be a string
454-
if (!angular.isString($scope.searchText)) $scope.searchText = '';
455+
456+
// When the searchText is not a string, force it to be an empty string.
457+
if (!angular.isString($scope.searchText)) {
458+
$scope.searchText = '';
459+
}
460+
455461
ctrl.hidden = shouldHide();
456-
if (!ctrl.hidden) handleQuery();
462+
463+
if (!ctrl.hidden) {
464+
handleQuery();
465+
}
466+
467+
evalAttr('ngFocus', { $event: $event });
457468
}
458469

459470
/**
@@ -897,4 +908,15 @@ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $mdTheming,
897908
});
898909
}
899910

911+
/**
912+
* Evaluates an attribute expression against the parent scope.
913+
* @param {String} attr Name of the attribute to be evaluated.
914+
* @param {Object?} locals Properties to be injected into the evaluation context.
915+
*/
916+
function evalAttr(attr, locals) {
917+
if ($attrs[attr]) {
918+
$scope.$parent.$eval($attrs[attr], locals || {});
919+
}
920+
}
921+
900922
}

src/components/autocomplete/js/autocompleteDirective.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,10 @@ function MdAutocomplete ($$mdSvgRegistry) {
259259
ng-model="$mdAutocompleteCtrl.scope.searchText"\
260260
ng-model-options="{ allowInvalid: true }"\
261261
ng-keydown="$mdAutocompleteCtrl.keydown($event)"\
262-
ng-blur="$mdAutocompleteCtrl.blur()"\
263-
' + (attr.mdNoAsterisk != null ? 'md-no-asterisk="' + attr.mdNoAsterisk + '"' : '') + '\
262+
ng-blur="$mdAutocompleteCtrl.blur($event)"\
264263
ng-focus="$mdAutocompleteCtrl.focus($event)"\
265264
aria-owns="ul-{{$mdAutocompleteCtrl.id}}"\
265+
' + (attr.mdNoAsterisk != null ? 'md-no-asterisk="' + attr.mdNoAsterisk + '"' : '') + '\
266266
' + (attr.mdSelectOnFocus != null ? 'md-select-on-focus=""' : '') + '\
267267
aria-label="{{floatingLabel}}"\
268268
aria-autocomplete="list"\
@@ -285,7 +285,7 @@ function MdAutocomplete ($$mdSvgRegistry) {
285285
ng-readonly="$mdAutocompleteCtrl.isReadonly"\
286286
ng-model="$mdAutocompleteCtrl.scope.searchText"\
287287
ng-keydown="$mdAutocompleteCtrl.keydown($event)"\
288-
ng-blur="$mdAutocompleteCtrl.blur()"\
288+
ng-blur="$mdAutocompleteCtrl.blur($event)"\
289289
ng-focus="$mdAutocompleteCtrl.focus($event)"\
290290
placeholder="{{placeholder}}"\
291291
aria-owns="ul-{{$mdAutocompleteCtrl.id}}"\

0 commit comments

Comments
 (0)