Skip to content

Commit aecbfaf

Browse files
authored
fix(angular): Call showReportDialog in root context (#11703)
This patch calls `showReportDialog` outside of the Angular context to prevent unnecessary view updates when asynchronous tasks are set up within the `showReportDialog` function. Also updates the documentation around `runOutsideAngular`
1 parent d8831c5 commit aecbfaf

File tree

2 files changed

+24
-18
lines changed

2 files changed

+24
-18
lines changed

packages/angular/src/errorhandler.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,18 @@ class SentryErrorHandler implements AngularErrorHandler {
121121
if (client && !this._registeredAfterSendEventHandler) {
122122
client.on('afterSendEvent', (event: Event) => {
123123
if (!event.type && event.event_id) {
124-
Sentry.showReportDialog({ ...this._options.dialogOptions, eventId: event.event_id });
124+
runOutsideAngular(() => {
125+
Sentry.showReportDialog({ ...this._options.dialogOptions, eventId: event.event_id! });
126+
});
125127
}
126128
});
127129

128130
// We only want to register this hook once in the lifetime of the error handler
129131
this._registeredAfterSendEventHandler = true;
130132
} else if (!client) {
131-
Sentry.showReportDialog({ ...this._options.dialogOptions, eventId });
133+
runOutsideAngular(() => {
134+
Sentry.showReportDialog({ ...this._options.dialogOptions, eventId });
135+
});
132136
}
133137
}
134138
}

packages/angular/src/zone.ts

+18-16
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
1-
// That's the `global.Zone` exposed when the `zone.js` package is used.
1+
// This would be exposed in the global environment whenever `zone.js` is
2+
// included in the `polyfills` configuration property. Starting from Angular 17,
3+
// users can opt-in to use zoneless change detection.
24
// eslint-disable-next-line @typescript-eslint/no-explicit-any
35
declare const Zone: any;
46

5-
// There're 2 types of Angular applications:
6-
// 1) zone-full (by default)
7-
// 2) zone-less
8-
// The developer can avoid importing the `zone.js` package and tells Angular that
9-
// he is responsible for running the change detection by himself. This is done by
10-
// "nooping" the zone through `CompilerOptions` when bootstrapping the root module.
7+
// In Angular 17 and future versions, zoneless support is forthcoming.
8+
// Therefore, it's advisable to safely check whether the `run` function is
9+
// available in the `<root>` context.
1110
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
12-
const isNgZoneEnabled = typeof Zone !== 'undefined' && !!Zone.current;
11+
const isNgZoneEnabled = typeof Zone !== 'undefined' && Zone.root && Zone.root.run;
1312

1413
/**
1514
* The function that does the same job as `NgZone.runOutsideAngular`.
15+
*
16+
* ⚠️ Note: All of the Sentry functionality called from inside the Angular
17+
* execution context must be wrapped in this function. Angular's rendering
18+
* relies on asynchronous tasks being scheduled within its execution context.
19+
* Since Sentry schedules tasks that do not interact with Angular's rendering,
20+
* it may prevent Angular from functioning reliably. Consequently, it may disrupt
21+
* processes such as server-side rendering or client-side hydration.
1622
*/
1723
export function runOutsideAngular<T>(callback: () => T): T {
18-
// The `Zone.root.run` basically will run the `callback` in the most parent zone.
19-
// Any asynchronous API used inside the `callback` won't catch Angular's zone
20-
// since `Zone.current` will reference `Zone.root`.
21-
// The Angular's zone is forked from the `Zone.root`. In this case, `zone.js` won't
22-
// trigger change detection, and `ApplicationRef.tick()` will not be run.
23-
// Caretaker note: we're using `Zone.root` except `NgZone.runOutsideAngular` since this
24-
// will require injecting the `NgZone` facade. That will create a breaking change for
25-
// projects already using the `@sentry/angular`.
24+
// Running the `callback` within the root execution context enables Angular
25+
// processes (such as SSR and hydration) to continue functioning normally without
26+
// timeouts and delays that could affect the user experience. This approach is
27+
// necessary because some of the Sentry functionality continues to run in the background.
2628
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
2729
return isNgZoneEnabled ? Zone.root.run(callback) : callback();
2830
}

0 commit comments

Comments
 (0)