From 6ef30537e56aadb055fd791d72ae74f84a002167 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 7 Apr 2025 14:08:34 -0700 Subject: [PATCH] feat(@schematics/angular): Add global error listeners to new app generation This commit adds the provider for global error listener in the browser. This is particularly useful for zoneless apps but also useful for Zone-based applications when errors happen outside the Angular zone. --- .../src/app/app.module.ts.template | 7 +++++-- .../src/app/app.config.ts.template | 7 +++++-- .../angular/service-worker/index_spec.ts | 2 +- .../angular/utility/standalone/rules_spec.ts | 19 +++++++++++++++---- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/schematics/angular/application/files/module-files/src/app/app.module.ts.template b/packages/schematics/angular/application/files/module-files/src/app/app.module.ts.template index 336e06590fbf..1fb2536ea0e7 100644 --- a/packages/schematics/angular/application/files/module-files/src/app/app.module.ts.template +++ b/packages/schematics/angular/application/files/module-files/src/app/app.module.ts.template @@ -1,4 +1,4 @@ -import { NgModule<% if(experimentalZoneless) { %>, provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core'; +import { NgModule, provideBrowserGlobalErrorListeners<% if(experimentalZoneless) { %>, provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; <% if (routing) { %> import { AppRoutingModule } from './app-routing.module';<% } %> @@ -12,7 +12,10 @@ import { App } from './app'; BrowserModule<% if (routing) { %>, AppRoutingModule<% } %> ], - providers: [<% if (experimentalZoneless) { %>provideExperimentalZonelessChangeDetection()<% } %>], + providers: [ + provideBrowserGlobalErrorListeners(), + <% if (experimentalZoneless) { %>provideExperimentalZonelessChangeDetection()<% } %> + ], bootstrap: [App] }) export class AppModule { } diff --git a/packages/schematics/angular/application/files/standalone-files/src/app/app.config.ts.template b/packages/schematics/angular/application/files/standalone-files/src/app/app.config.ts.template index 98551b4f24d0..12a5e6733e15 100644 --- a/packages/schematics/angular/application/files/standalone-files/src/app/app.config.ts.template +++ b/packages/schematics/angular/application/files/standalone-files/src/app/app.config.ts.template @@ -1,8 +1,11 @@ -import { ApplicationConfig, <% if(!experimentalZoneless) { %>provideZoneChangeDetection<% } else { %>provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core';<% if (routing) { %> +import { ApplicationConfig, provideBrowserGlobalErrorListeners, <% if(!experimentalZoneless) { %>provideZoneChangeDetection<% } else { %>provideExperimentalZonelessChangeDetection<% } %> } from '@angular/core';<% if (routing) { %> import { provideRouter } from '@angular/router'; import { routes } from './app.routes';<% } %> export const appConfig: ApplicationConfig = { - providers: [<% if(experimentalZoneless) { %>provideExperimentalZonelessChangeDetection()<% } else { %>provideZoneChangeDetection({ eventCoalescing: true })<% } %><% if (routing) {%>, provideRouter(routes)<% } %>] + providers: [ + provideBrowserGlobalErrorListeners(), + <% if(experimentalZoneless) { %>provideExperimentalZonelessChangeDetection()<% } else { %>provideZoneChangeDetection({ eventCoalescing: true })<% } %>, + <% if (routing) {%>provideRouter(routes),<% } %>] }; diff --git a/packages/schematics/angular/service-worker/index_spec.ts b/packages/schematics/angular/service-worker/index_spec.ts index 87f3a9aafc46..7b7b4ba36e76 100644 --- a/packages/schematics/angular/service-worker/index_spec.ts +++ b/packages/schematics/angular/service-worker/index_spec.ts @@ -139,7 +139,7 @@ describe('Service Worker Schematic', () => { const tree = await schematicRunner.runSchematic('service-worker', defaultOptions, appTree); const content = tree.readContent('/projects/bar/src/app/app.config.ts'); expect(content).toContain( - `import { ApplicationConfig, provideZoneChangeDetection, isDevMode } from '@angular/core';`, + `import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection, isDevMode } from '@angular/core';`, ); }); diff --git a/packages/schematics/angular/utility/standalone/rules_spec.ts b/packages/schematics/angular/utility/standalone/rules_spec.ts index 0eed95662e2b..eab42c3d523f 100644 --- a/packages/schematics/angular/utility/standalone/rules_spec.ts +++ b/packages/schematics/angular/utility/standalone/rules_spec.ts @@ -423,7 +423,10 @@ describe('standalone utilities', () => { const content = readFile('app/app.module.ts'); assertContains(content, `import { SOME_TOKEN } from '@my/module';`); - assertContains(content, `providers: [{ provide: SOME_TOKEN, useValue: 123 }]`); + assertContains( + content, + `providers: [provideBrowserGlobalErrorListeners(),{ provide: SOME_TOKEN, useValue: 123 },]`, + ); }); it('should add a root provider to a standalone app', async () => { @@ -442,7 +445,11 @@ describe('standalone utilities', () => { assertContains(content, `import { provideModule } from '@my/module';`); assertContains( content, - `providers: [provideZoneChangeDetection({ eventCoalescing:true }),provideModule([])]`, + `providers: [ + provideBrowserGlobalErrorListeners(), + provideZoneChangeDetection({ eventCoalescing:true }), + provideModule([]), + ]`, ); }); @@ -453,11 +460,12 @@ describe('standalone utilities', () => { host.overwrite( getPathWithinProject(configPath), ` - import { ApplicationConfig } from '@angular/core'; + import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core'; import { provideRouter } from '@angular/router'; export const appConfig: ApplicationConfig = { providers: [ + provideBrowserGlobalErrorListeners(), provideRouter([]), ] }; @@ -474,7 +482,10 @@ describe('standalone utilities', () => { const content = readFile('app/app.config.ts'); assertContains(content, `import { provideModule } from '@my/module';`); - assertContains(content, `providers: [provideRouter([]),provideModule([]),]`); + assertContains( + content, + `providers: [provideBrowserGlobalErrorListeners(), provideRouter([]),provideModule([]),]`, + ); }); }); });