From a42f8a0d5b67f46af354b23f14b88d6893a183ec Mon Sep 17 00:00:00 2001 From: Martin Staffa Date: Fri, 13 Jul 2018 12:44:33 +0200 Subject: [PATCH] fix(ngEventDirs): pass error in handler to $exceptionHandler when event was triggered in a digest This ensures that the error handling is the same for events triggered inside and outside a digest. --- src/ng/directive/ngEventDirs.js | 9 ++- test/ng/directive/ngEventDirsSpec.js | 89 +++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/ng/directive/ngEventDirs.js b/src/ng/directive/ngEventDirs.js index 94a619321..faee43da4 100644 --- a/src/ng/directive/ngEventDirs.js +++ b/src/ng/directive/ngEventDirs.js @@ -50,7 +50,7 @@ forEach( 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), function(eventName) { var directiveName = directiveNormalize('ng-' + eventName); - ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { + ngEventDirectives[directiveName] = ['$parse', '$rootScope', '$exceptionHandler', function($parse, $rootScope, $exceptionHandler) { return { restrict: 'A', compile: function($element, attr) { @@ -64,12 +64,17 @@ forEach( var callback = function() { fn(scope, {$event: event}); }; + if (!$rootScope.$$phase) { scope.$apply(callback); } else if (forceAsyncEvents[eventName]) { scope.$evalAsync(callback); } else { - callback(); + try { + callback(); + } catch (error) { + $exceptionHandler(error); + } } }); }; diff --git a/test/ng/directive/ngEventDirsSpec.js b/test/ng/directive/ngEventDirsSpec.js index 8f08d748f..4a533a471 100644 --- a/test/ng/directive/ngEventDirsSpec.js +++ b/test/ng/directive/ngEventDirsSpec.js @@ -148,7 +148,6 @@ describe('event directives', function() { expect($rootScope.blur).toHaveBeenCalledOnce(); expect(element.val()).toBe('newValue'); })); - }); @@ -190,4 +189,92 @@ describe('event directives', function() { expect($rootScope.click).toHaveBeenCalledOnce(); expect(watchedVal).toEqual('newValue'); })); + + + describe('throwing errors in event handlers', function() { + + it('should not stop execution if the event is triggered outside a digest', function() { + + module(function($exceptionHandlerProvider) { + $exceptionHandlerProvider.mode('log'); + }); + + inject(function($rootScope, $compile, $exceptionHandler, $log) { + + element = $compile('')($rootScope); + expect($log.assertEmpty()); + $rootScope.click = function() { + throw new Error('listener error'); + }; + + $rootScope.do = function() { + element.triggerHandler('click'); + $log.log('done'); + }; + + $rootScope.do(); + + expect($exceptionHandler.errors).toEqual([Error('listener error')]); + expect($log.log.logs).toEqual([['done']]); + $log.reset(); + }); + }); + + + it('should not stop execution if the event is triggered inside a digest', function() { + + module(function($exceptionHandlerProvider) { + $exceptionHandlerProvider.mode('log'); + }); + + inject(function($rootScope, $compile, $exceptionHandler, $log) { + + element = $compile('')($rootScope); + expect($log.assertEmpty()); + $rootScope.click = function() { + throw new Error('listener error'); + }; + + $rootScope.do = function() { + element.triggerHandler('click'); + $log.log('done'); + }; + + $rootScope.$apply(function() { + $rootScope.do(); + }); + + expect($exceptionHandler.errors).toEqual([Error('listener error')]); + expect($log.log.logs).toEqual([['done']]); + $log.reset(); + }); + }); + + + it('should not stop execution if the event is triggered in a watch expression function', function() { + + module(function($exceptionHandlerProvider) { + $exceptionHandlerProvider.mode('log'); + }); + + inject(function($rootScope, $compile, $exceptionHandler, $log) { + + element = $compile('')($rootScope); + $rootScope.click = function() { + throw new Error('listener error'); + }; + + $rootScope.$watch(function() { + element.triggerHandler('click'); + $log.log('done'); + }); + + $rootScope.$digest(); + + expect($exceptionHandler.errors).toEqual([Error('listener error'), Error('listener error')]); + expect($log.log.logs).toEqual([['done'], ['done']]); + $log.reset(); + }); + }); + }); });