diff --git a/packages/core/src/render3/after_render_hooks.ts b/packages/core/src/render3/after_render_hooks.ts index c583fe9d6f40e9..11d0d96eaab786 100644 --- a/packages/core/src/render3/after_render_hooks.ts +++ b/packages/core/src/render3/after_render_hooks.ts @@ -10,6 +10,7 @@ import {assertInInjectionContext, Injector, ɵɵdefineInjectable} from '../di'; import {inject} from '../di/injector_compatibility'; import {RuntimeError, RuntimeErrorCode} from '../errors'; import {DestroyRef} from '../linker/destroy_ref'; +import {NgZone} from '../zone'; import {isPlatformBrowser} from './util/misc_utils'; @@ -188,6 +189,7 @@ class AfterRenderCallback { * Implements `afterRender` and `afterNextRender` callback manager logic. */ export class AfterRenderEventManager { + private zone = inject(NgZone); private callbacks = new Set(); private deferredCallbacks = new Set(); private renderDepth = 0; @@ -219,9 +221,11 @@ export class AfterRenderEventManager { if (this.renderDepth === 0) { try { this.runningCallbacks = true; - for (const callback of this.callbacks) { - callback.invoke(); - } + this.zone.runOutsideAngular(() => { + for (const callback of this.callbacks) { + callback.invoke(); + } + }); } finally { this.runningCallbacks = false; for (const callback of this.deferredCallbacks) { diff --git a/packages/core/test/acceptance/after_render_hook_spec.ts b/packages/core/test/acceptance/after_render_hook_spec.ts index 5e3e6b97619b1e..eeb7fcf40993ef 100644 --- a/packages/core/test/acceptance/after_render_hook_spec.ts +++ b/packages/core/test/acceptance/after_render_hook_spec.ts @@ -7,7 +7,7 @@ */ import {PLATFORM_BROWSER_ID, PLATFORM_SERVER_ID} from '@angular/common/src/platform_id'; -import {afterNextRender, afterRender, AfterRenderRef, ChangeDetectorRef, Component, inject, Injector, PLATFORM_ID, ViewContainerRef} from '@angular/core'; +import {afterNextRender, afterRender, AfterRenderRef, ChangeDetectorRef, Component, inject, Injector, NgZone, PLATFORM_ID, ViewContainerRef} from '@angular/core'; import {TestBed} from '@angular/core/testing'; describe('after render hooks', () => { @@ -225,6 +225,29 @@ describe('after render hooks', () => { expect(outerHookCount).toBe(3); expect(innerHookCount).toBe(2); }); + + it('should run outside of the Angular zone', () => { + const zoneLog: boolean[] = []; + + @Component({selector: 'comp'}) + class Comp { + constructor() { + afterRender(() => { + zoneLog.push(NgZone.isInAngularZone()); + }); + } + } + + TestBed.configureTestingModule({ + declarations: [Comp], + ...COMMON_CONFIGURATION, + }); + const fixture = TestBed.createComponent(Comp); + + expect(zoneLog).toEqual([]); + fixture.detectChanges(); + expect(zoneLog).toEqual([false]); + }); }); describe('afterNextRender', () => { @@ -431,6 +454,29 @@ describe('after render hooks', () => { expect(outerHookCount).toBe(1); expect(innerHookCount).toBe(1); }); + + it('should run outside of the Angular zone', () => { + const zoneLog: boolean[] = []; + + @Component({selector: 'comp'}) + class Comp { + constructor() { + afterNextRender(() => { + zoneLog.push(NgZone.isInAngularZone()); + }); + } + } + + TestBed.configureTestingModule({ + declarations: [Comp], + ...COMMON_CONFIGURATION, + }); + const fixture = TestBed.createComponent(Comp); + + expect(zoneLog).toEqual([]); + fixture.detectChanges(); + expect(zoneLog).toEqual([false]); + }); }); });