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.
This commit is contained in:
Martin Staffa
2018-07-13 12:44:33 +02:00
committed by Martin Staffa
parent 6b0193e4d9
commit a42f8a0d5b
2 changed files with 95 additions and 3 deletions
+7 -2
View File
@@ -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);
}
}
});
};
+88 -1
View File
@@ -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('<button ng-click="click()">Click</button>')($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('<button ng-click="click()">Click</button>')($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('<button ng-click="click()">Click</button>')($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();
});
});
});
});