From eedf2a399d96eb0627150ef9bde7d65fc2c6eaca Mon Sep 17 00:00:00 2001 From: Rainer Hahnekamp Date: Wed, 3 Jan 2024 20:58:01 +0100 Subject: [PATCH 1/3] fix(signals): run `onDestroy` outside of injection context --- modules/signals/spec/signal-store.spec.ts | 4 +++- modules/signals/src/signal-store.ts | 14 ++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/signals/spec/signal-store.spec.ts b/modules/signals/spec/signal-store.spec.ts index 39a6e79db8..b5719016f5 100644 --- a/modules/signals/spec/signal-store.spec.ts +++ b/modules/signals/spec/signal-store.spec.ts @@ -268,6 +268,8 @@ describe('signalStore', () => { expect(message).toBe('onDestroy'); }); + // FIX: injection context will be provided for `onDestroy` in a separate PR + // see https://github.com/ngrx/platform/pull/4196#issuecomment-1875228588 it('executes hooks in injection context', () => { const messages: string[] = []; const TOKEN = new InjectionToken('TOKEN', { @@ -281,7 +283,7 @@ describe('signalStore', () => { messages.push('onInit'); }, onDestroy() { - inject(TOKEN); + // inject(TOKEN); messages.push('onDestroy'); }, }) diff --git a/modules/signals/src/signal-store.ts b/modules/signals/src/signal-store.ts index 3125d3fc24..e5570d9932 100644 --- a/modules/signals/src/signal-store.ts +++ b/modules/signals/src/signal-store.ts @@ -325,16 +325,14 @@ export function signalStore( (this as any)[key] = props[key]; } - if (hooks.onInit) { - hooks.onInit(); - } + const { onInit, onDestroy } = hooks; - if (hooks.onDestroy) { - const injector = inject(Injector); + if (onInit) { + onInit(); + } - inject(DestroyRef).onDestroy(() => { - runInInjectionContext(injector, hooks.onDestroy!); - }); + if (onDestroy) { + inject(DestroyRef).onDestroy(onDestroy); } } } From 95616c8cec8096715f720dc2d02812fb0c1ad9d3 Mon Sep 17 00:00:00 2001 From: Rainer Hahnekamp Date: Wed, 3 Jan 2024 22:40:56 +0100 Subject: [PATCH 2/3] fix(signals): add test verifying `onDestroy` fails if it accesses DI --- modules/signals/spec/signal-store.spec.ts | 17 +++++++++++++++++ modules/signals/src/signal-store.ts | 12 ++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/modules/signals/spec/signal-store.spec.ts b/modules/signals/spec/signal-store.spec.ts index b5719016f5..90431147e8 100644 --- a/modules/signals/spec/signal-store.spec.ts +++ b/modules/signals/spec/signal-store.spec.ts @@ -295,6 +295,23 @@ describe('signalStore', () => { destroy(); expect(messages).toEqual(['onInit', 'onDestroy']); }); + + it('fails if onDestroy accesses DI', () => { + const TOKEN = new InjectionToken('TOKEN', { + providedIn: 'root', + factory: () => 'ngrx', + }); + const Store = signalStore( + withHooks({ + onDestroy() { + inject(TOKEN); + }, + }) + ); + const { destroy } = createLocalService(Store); + + expect(() => destroy()).toThrowError('NG0203'); + }); }); describe('composition', () => { diff --git a/modules/signals/src/signal-store.ts b/modules/signals/src/signal-store.ts index e5570d9932..56c83d38a9 100644 --- a/modules/signals/src/signal-store.ts +++ b/modules/signals/src/signal-store.ts @@ -1,21 +1,13 @@ -import { - DestroyRef, - inject, - Injectable, - Injector, - runInInjectionContext, - signal, - Type, -} from '@angular/core'; +import { DestroyRef, inject, Injectable, signal, Type } from '@angular/core'; import { STATE_SIGNAL, StateSignal } from './state-signal'; import { EmptyFeatureResult, InnerSignalStore, MergeFeatureResults, - SignalStoreProps, SignalStoreConfig, SignalStoreFeature, SignalStoreFeatureResult, + SignalStoreProps, } from './signal-store-models'; import { Prettify } from './ts-helpers'; From 66994ed9546dc476cfc759305cc0fbe52608d8ec Mon Sep 17 00:00:00 2001 From: Rainer Hahnekamp Date: Fri, 5 Jan 2024 10:21:21 +0100 Subject: [PATCH 3/3] test(signals): verify that root-provided onDestroy doesn't fail --- modules/signals/spec/signal-store.spec.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/signals/spec/signal-store.spec.ts b/modules/signals/spec/signal-store.spec.ts index 90431147e8..ce2a618cb6 100644 --- a/modules/signals/spec/signal-store.spec.ts +++ b/modules/signals/spec/signal-store.spec.ts @@ -296,21 +296,20 @@ describe('signalStore', () => { expect(messages).toEqual(['onInit', 'onDestroy']); }); - it('fails if onDestroy accesses DI', () => { - const TOKEN = new InjectionToken('TOKEN', { - providedIn: 'root', - factory: () => 'ngrx', - }); + it('succeeds with onDestroy and providedIn: root', () => { + const messages: string[] = []; const Store = signalStore( + { providedIn: 'root' }, withHooks({ onDestroy() { - inject(TOKEN); + messages.push('ending...'); }, }) ); - const { destroy } = createLocalService(Store); + TestBed.inject(Store); + TestBed.resetTestEnvironment(); - expect(() => destroy()).toThrowError('NG0203'); + expect(messages).toEqual(['ending...']); }); });