From 79ae35577efc7d2fb2fbf87e7783daf938c576ff Mon Sep 17 00:00:00 2001 From: arturovt Date: Tue, 28 Jan 2025 08:50:36 +0200 Subject: [PATCH] fix(core): check whether application is destroyed before initializing event replay (#59789) In this commit, we check whether the application is destroyed before initializing event replay. The application may be destroyed before it becomes stable, so when the `whenStable` resolves, the injector might already be in a destroyed state. As a result, calling `injector.get` would throw an error indicating that the injector has already been destroyed. PR Close #59789 --- packages/core/src/hydration/event_replay.ts | 16 ++++- .../platform-server/test/event_replay_spec.ts | 68 +++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/packages/core/src/hydration/event_replay.ts b/packages/core/src/hydration/event_replay.ts index 75d20460777..fb18b915e1a 100644 --- a/packages/core/src/hydration/event_replay.ts +++ b/packages/core/src/hydration/event_replay.ts @@ -98,8 +98,8 @@ export function withEventReplay(): Provider[] { { provide: ENVIRONMENT_INITIALIZER, useValue: () => { - const injector = inject(Injector); - const appRef = injector.get(ApplicationRef); + const appRef = inject(ApplicationRef); + const {injector} = appRef; // We have to check for the appRef here due to the possibility of multiple apps // being present on the same page. We only want to enable event replay for the // apps that actually want it. @@ -123,8 +123,8 @@ export function withEventReplay(): Provider[] { provide: APP_BOOTSTRAP_LISTENER, useFactory: () => { const appId = inject(APP_ID); - const injector = inject(Injector); const appRef = inject(ApplicationRef); + const {injector} = appRef; return () => { // We have to check for the appRef here due to the possibility of multiple apps @@ -155,6 +155,16 @@ export function withEventReplay(): Provider[] { // of the application is completed. This timing is similar to the unclaimed // dehydrated views cleanup timing. appRef.whenStable().then(() => { + // Note: we have to check whether the application is destroyed before + // performing other operations with the `injector`. + // The application may be destroyed **before** it becomes stable, so when + // the `whenStable` resolves, the injector might already be in + // a destroyed state. Thus, calling `injector.get` would throw an error + // indicating that the injector has already been destroyed. + if (appRef.destroyed) { + return; + } + const eventContractDetails = injector.get(JSACTION_EVENT_CONTRACT); initEventReplay(eventContractDetails, injector); const jsActionMap = injector.get(JSACTION_BLOCK_ELEMENT_MAP); diff --git a/packages/platform-server/test/event_replay_spec.ts b/packages/platform-server/test/event_replay_spec.ts index aec888abb82..5b951a53236 100644 --- a/packages/platform-server/test/event_replay_spec.ts +++ b/packages/platform-server/test/event_replay_spec.ts @@ -13,8 +13,11 @@ import { Directive, ErrorHandler, HostListener, + inject, + PendingTasks, PLATFORM_ID, } from '@angular/core'; +import {isPlatformBrowser} from '@angular/common'; import {withEventReplay} from '@angular/platform-browser'; import {EventPhase} from '@angular/core/primitives/event-dispatch'; @@ -363,6 +366,71 @@ describe('event replay', () => { expect(getAppContents(html)).toContain('