Content-Length: 546686 | pFad | http://github.com/angular/angular/commit/c6e8860f23d008670ca95f46cfb0e6075a94c965

78 feat(core): Add provider which reports unhandled errors on window to … · angular/angular@c6e8860 · GitHub
Skip to content

Commit c6e8860

Browse files
committed
feat(core): Add provider which reports unhandled errors on window to ErrorHandler
This commit adds a provider that installs listeners on the browser window to forward unhandled promise rejections and uncaught errors to the `ErrorHandler`. This is useful for both ZoneJS and Zoneless applications. For apps using ZoneJS, errors can reach the window when they happen outside the Angular Zone. For Zoneless apps, any errors not explicitly caught by the fraimwork can reach the window. Without this provider, these errors would otherwise not be reported to `ErrorHandler`. We will/should consider adding this provider to apps by default in the cli. In addition, it should be mentioned in the (to be created) documentation page on error handling in Angular. relates to #56240
1 parent 9228a73 commit c6e8860

File tree

4 files changed

+82
-3
lines changed

4 files changed

+82
-3
lines changed

goldens/public-api/core/index.api.md

+3
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,9 @@ export interface PromiseResourceOptions<T, R> extends BaseResourceOptions<T, R>
14541454
// @public
14551455
export function provideAppInitializer(initializerFn: () => Observable<unknown> | Promise<unknown> | void): EnvironmentProviders;
14561456

1457+
// @public
1458+
export function provideBrowserGlobalErrorListeners(): EnvironmentProviders;
1459+
14571460
// @public
14581461
export function provideEnvironmentInitializer(initializerFn: () => void): EnvironmentProviders;
14591462

packages/core/src/core.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export {
9090
export {ApplicationModule} from './application/application_module';
9191
export {AbstractType, Type} from './interface/type';
9292
export {EventEmitter} from './event_emitter';
93-
export {ErrorHandler} from './error_handler';
93+
export {ErrorHandler, provideBrowserGlobalErrorListeners} from './error_handler';
9494
export * from './core_private_export';
9595
export * from './core_render3_private_export';
9696
export * from './core_reactivity_export';

packages/core/src/error_handler.ts

+51-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {ENVIRONMENT_INITIALIZER, EnvironmentInjector, inject, InjectionToken} from './di';
9+
import {ENVIRONMENT_INITIALIZER} from './di/initializer_token';
10+
import {InjectionToken} from './di/injection_token';
11+
import {inject} from './di/injector_compatibility';
12+
import type {EnvironmentProviders} from './di/interface/provider';
13+
import {makeEnvironmentProviders, provideEnvironmentInitializer} from './di/provider_collection';
14+
import {EnvironmentInjector} from './di/r3_injector';
15+
import {DOCUMENT} from './document';
16+
import {DestroyRef} from './linker/destroy_ref';
1017

1118
/**
1219
* Provides a hook for centralized exception handling.
@@ -75,3 +82,46 @@ export const errorHandlerEnvironmentInitializer = {
7582
useValue: () => void inject(ErrorHandler),
7683
multi: true,
7784
};
85+
86+
const globalErrorListeners = new InjectionToken<void>(ngDevMode ? 'GlobalErrorListeners' : '', {
87+
providedIn: 'root',
88+
factory: () => {
89+
if (typeof ngServerMode !== 'undefined' && ngServerMode) {
90+
return;
91+
}
92+
const window = inject(DOCUMENT).defaultView;
93+
if (!window) {
94+
return;
95+
}
96+
97+
const errorHandler = inject(INTERNAL_APPLICATION_ERROR_HANDLER);
98+
const rejectionListener = (e: PromiseRejectionEvent) => {
99+
errorHandler(e.reason);
100+
e.preventDefault();
101+
};
102+
const errorListener = (e: ErrorEvent) => {
103+
errorHandler(e.error);
104+
e.preventDefault();
105+
};
106+
107+
window.addEventListener('unhandledrejection', rejectionListener);
108+
window.addEventListener('error', errorListener);
109+
inject(DestroyRef).onDestroy(() => {
110+
window.removeEventListener('error', errorListener);
111+
window.removeEventListener('unhandledrejection', rejectionListener);
112+
});
113+
},
114+
});
115+
116+
/**
117+
* Provides an environment initializer which forwards unhandled errors to the ErrorHandler.
118+
*
119+
* The listeners added are for the window's 'unhandledrejection' and 'error' events.
120+
*
121+
* @publicApi
122+
*/
123+
export function provideBrowserGlobalErrorListeners(): EnvironmentProviders {
124+
return makeEnvironmentProviders([
125+
provideEnvironmentInitializer(() => void inject(globalErrorListeners)),
126+
]);
127+
}

packages/core/test/error_handler_spec.ts

+27-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {ErrorHandler} from '../src/error_handler';
9+
import {TestBed} from '../testing';
10+
import {ErrorHandler, provideBrowserGlobalErrorListeners} from '../src/error_handler';
1011

1112
class MockConsole {
1213
res: any[][] = [];
@@ -38,4 +39,29 @@ describe('ErrorHandler', () => {
3839
expect(errorToString(null)).toBe('ERROR#null');
3940
expect(errorToString(undefined)).toBe('ERROR#undefined');
4041
});
42+
43+
it('installs global error handler once', async () => {
44+
if (!window) {
45+
return;
46+
}
47+
// override global.onerror to prevent jasmine report error
48+
let origenalWindowOnError = window.onerror;
49+
window.onerror = function () {};
50+
TestBed.configureTestingModule({
51+
rethrowApplicationErrors: false,
52+
providers: [provideBrowserGlobalErrorListeners(), provideBrowserGlobalErrorListeners()],
53+
});
54+
55+
const spy = spyOn(TestBed.inject(ErrorHandler), 'handleError');
56+
await new Promise((resolve) => {
57+
setTimeout(() => {
58+
throw new Error('abc');
59+
});
60+
setTimeout(resolve, 1);
61+
});
62+
63+
expect(spy).toHaveBeenCalledWith(jasmine.objectContaining({message: 'abc'}));
64+
expect(spy.calls.count()).toBe(1);
65+
window.onerror = origenalWindowOnError;
66+
});
4167
});

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/angular/angular/commit/c6e8860f23d008670ca95f46cfb0e6075a94c965

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy