From 04bffaa41c262ab23294bfa62fb09e547092bf83 Mon Sep 17 00:00:00 2001 From: AleksanderBodurri Date: Sat, 12 Aug 2023 03:12:42 -0400 Subject: [PATCH] refactor(core): implement __ignore_ng_zone__ flag (#51339) This flag allows message event listeners to prevent callbacks from executing within the NgZone if they contain a special `__ignore_ng_zone__` flag. This functionality is built with Angular DevTools in mind, where it prevents an infinite change detection loop in inspecting applications that have message event listeners: CD -> Inspected app emits componentTreeDirty event to DevTools -> DevTools emits event to get new component Tree from Inspected app -> Inspected app message event listener fires -> CD PR Close #51339 --- packages/core/src/zone/ng_zone.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/core/src/zone/ng_zone.ts b/packages/core/src/zone/ng_zone.ts index a2fe01cb804..1fe5c47113e 100644 --- a/packages/core/src/zone/ng_zone.ts +++ b/packages/core/src/zone/ng_zone.ts @@ -419,6 +419,10 @@ function forkInnerZoneWithAngularBehavior(zone: NgZonePrivate) { onInvokeTask: (delegate: ZoneDelegate, current: Zone, target: Zone, task: Task, applyThis: any, applyArgs: any): any => { + if (shouldBeIgnoredByZone(applyArgs)) { + return delegate.invokeTask(target, task, applyThis, applyArgs); + } + try { onEnter(zone); return delegate.invokeTask(target, task, applyThis, applyArgs); @@ -585,3 +589,18 @@ export function isStableFactory() { }); return merge(isCurrentlyStable, isStable.pipe(share())); } + +function shouldBeIgnoredByZone(applyArgs: unknown): boolean { + if (!Array.isArray(applyArgs)) { + return false; + } + + // We should only ever get 1 arg passed through to invokeTask. + // Short circuit here incase that behavior changes. + if (applyArgs.length !== 1) { + return false; + } + + // Prevent triggering change detection when the __ignore_ng_zone__ flag is detected. + return applyArgs[0].data?.['__ignore_ng_zone__'] === true; +}