diff --git a/src/ng/directive/ngEventDirs.js b/src/ng/directive/ngEventDirs.js index e69e6c8c61b0..e833c8e833d5 100644 --- a/src/ng/directive/ngEventDirs.js +++ b/src/ng/directive/ngEventDirs.js @@ -31,14 +31,14 @@ */ /* - * A directive that allows creation of custom onclick handlers that are defined as angular + * A directive that allows creation of custom event handlers that are defined as angular * expressions and are compiled and executed within the current scope. * * Events that are handled via these handler are always configured not to propagate further. */ var ngEventDirectives = {}; forEach( - 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), + 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit copy cut paste'.split(' '), function(name) { var directiveName = directiveNormalize('ng-' + name); ngEventDirectives[directiveName] = ['$parse', function($parse) { @@ -58,6 +58,32 @@ forEach( } ); +/* + * Focus and blur need to use $evalAsync to prevent $rootScope.inprog errors caused by other event directives triggering + * focus() and blur() respectively + * + */ +forEach( + 'focus blur'.split(' '), + function(name) { + var directiveName = directiveNormalize('ng-' + name); + ngEventDirectives[directiveName] = ['$parse', function($parse) { + return { + compile: function($element, attr) { + var fn = $parse(attr[directiveName]); + return function(scope, element, attr) { + element.on(lowercase(name), function(event) { + scope.$evalAsync(function() { + fn(scope, {$event:event}); + }); + }); + }; + } + }; + }]; + } +); + /** * @ngdoc directive * @name ngDblclick diff --git a/test/ng/directive/ngEventDirsSpec.js b/test/ng/directive/ngEventDirsSpec.js index 5b73c2dd6a8b..7dda5a0169d4 100644 --- a/test/ng/directive/ngEventDirsSpec.js +++ b/test/ng/directive/ngEventDirsSpec.js @@ -39,4 +39,38 @@ describe('event directives', function() { expect($rootScope.formSubmitted).toEqual('foo'); })); }); + + describe('ngBlur', function() { + iit('should get called when ngKeydown triggers blur', inject(function($rootScope, $compile) { + $rootScope.blur = function() { + browserTrigger(element, 'blur'); + } + + element = $compile('')($rootScope); + + $rootScope.$digest(); + expect($rootScope.blurred).not.toBeDefined(); + + browserTrigger(element, 'keydown'); + expect($rootScope.blurred).toEqual(true); + })); + }); + + describe('ngFocus', function() { + iit('should get called when ngClick triggers focus', inject(function($rootScope, $compile) { + $rootScope.focus = function() { + browserTrigger(element.children()[0], 'focus'); + } + + element = $compile('