Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 082f17c

Browse files
committed
feat(state): allow to pass injector to rxEffects
1 parent ad3234a commit 082f17c

File tree

1 file changed

+116
-76
lines changed

1 file changed

+116
-76
lines changed

libs/state/effects/src/lib/rx-effects.ts

Lines changed: 116 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,43 @@ import {
33
DestroyRef,
44
ErrorHandler,
55
inject,
6+
Injector,
7+
runInInjectionContext,
68
} from '@angular/core';
79
import { from, Subscription } from 'rxjs';
810
import { SideEffectFnOrObserver, SideEffectObservable } from './types';
911

1012
interface RxEffects {
1113
register<T>(
1214
observable: SideEffectObservable<T>,
13-
sideEffectOrObserver?: SideEffectFnOrObserver<T>
15+
sideEffectOrObserver?: SideEffectFnOrObserver<T>,
1416
): Fn;
1517
onDestroy: (fn: Fn) => Fn;
1618
}
1719

1820
type Fn = () => void;
1921

2022
export type RxEffectsSetupFn = (
21-
cfg: Pick<RxEffects, 'register' | 'onDestroy'>
23+
cfg: Pick<RxEffects, 'register' | 'onDestroy'>,
2224
) => void;
2325

26+
export type RxEffectsOptions = {
27+
injector?: Injector;
28+
};
29+
30+
function getInjectorFromOptions<
31+
SetupFn extends Function,
32+
Options extends { injector?: Injector },
33+
>(setupFnOrOptions?: SetupFn | Options, options?: Options) {
34+
if (options) {
35+
return options.injector;
36+
}
37+
if (setupFnOrOptions && typeof setupFnOrOptions !== 'function') {
38+
return setupFnOrOptions.injector;
39+
}
40+
return undefined;
41+
}
42+
2443
/**
2544
* @description
2645
* Functional way to setup observable based side effects with RxEffects.
@@ -55,83 +74,104 @@ export type RxEffectsSetupFn = (
5574
* @docsPage RxEffects
5675
*
5776
*/
58-
export function rxEffects(setupFn?: RxEffectsSetupFn): RxEffects {
59-
assertInInjectionContext(rxEffects);
60-
const errorHandler = inject(ErrorHandler, { optional: true });
61-
const destroyRef = inject(DestroyRef);
62-
const runningEffects: Subscription[] = [];
63-
destroyRef.onDestroy(() => runningEffects.forEach((ef) => ef.unsubscribe()));
64-
65-
/**
66-
* Subscribe to observables and trigger side effect.
67-
*
68-
* @example
69-
*
70-
* /@Component({
71-
* template: `<button name="save" (click)="save()">Save</button>`
72-
* })
73-
* class ListComponent {
74-
* private ef = rxEffects(({register}) => {
75-
* register(timer(0, this.backupInterval), console.log));
76-
* }
77-
* }
78-
*
79-
* @param {SideEffectObservable} obs$ Source observable input
80-
* @param {SideEffectFnOrObserver} sideEffect Observer object
81-
*
82-
* @return {Function} - unregisterFn
83-
*/
84-
function register<T>(
85-
obs$: SideEffectObservable<T>,
86-
sideEffect?: SideEffectFnOrObserver<T>
87-
): () => void {
88-
const observer =
89-
typeof sideEffect === 'object'
90-
? {
91-
...sideEffect,
92-
// preserve original logic
93-
error: (e: unknown) => {
94-
sideEffect.error?.(e);
95-
errorHandler?.handleError(e);
96-
},
97-
}
98-
: {
99-
next: sideEffect,
100-
error: (e: unknown) => errorHandler?.handleError(e),
101-
};
102-
const sub = from(obs$).subscribe(observer);
103-
runningEffects.push(sub);
104-
return () => sub.unsubscribe();
77+
export function rxEffects(): RxEffects;
78+
export function rxEffects(setupFn: RxEffectsSetupFn): RxEffects;
79+
export function rxEffects(options: RxEffectsOptions): RxEffects;
80+
export function rxEffects(
81+
setupFn: RxEffectsSetupFn,
82+
options: RxEffectsOptions,
83+
): RxEffects;
84+
export function rxEffects(
85+
setupFnOrOptions?: RxEffectsSetupFn | RxEffectsOptions,
86+
options?: RxEffectsOptions,
87+
): RxEffects {
88+
const injectorFromOptions = getInjectorFromOptions(setupFnOrOptions, options);
89+
if (!injectorFromOptions) {
90+
assertInInjectionContext(rxEffects);
10591
}
10692

107-
/**
108-
* Register custom cleanup logic.
109-
*
110-
* @example
111-
*
112-
* /@Component({
113-
* template: `<button name="save" (click)="save()">Save</button>`
114-
* })
115-
* class ListComponent {
116-
* private ef = rxEffects(({onDestroy}) => {
117-
* onDestroy(() => console.log('done'));
118-
* }
119-
* }
120-
*
121-
* @param {Fn} callback onDestroy callback
122-
*
123-
* @return {Fn} unregisterFn
124-
*/
125-
function onDestroy(callback: Fn): Fn {
126-
return destroyRef.onDestroy(callback);
127-
}
93+
const injector = injectorFromOptions ?? inject(Injector);
94+
95+
return runInInjectionContext(injector, () => {
96+
const errorHandler = inject(ErrorHandler, { optional: true });
97+
const destroyRef = inject(DestroyRef);
98+
const runningEffects: Subscription[] = [];
99+
destroyRef.onDestroy(() =>
100+
runningEffects.forEach((ef) => ef.unsubscribe()),
101+
);
102+
103+
/**
104+
* Subscribe to observables and trigger side effect.
105+
*
106+
* @example
107+
*
108+
* /@Component({
109+
* template: `<button name="save" (click)="save()">Save</button>`
110+
* })
111+
* class ListComponent {
112+
* private ef = rxEffects(({register}) => {
113+
* register(timer(0, this.backupInterval), console.log));
114+
* }
115+
* }
116+
*
117+
* @param {SideEffectObservable} obs$ Source observable input
118+
* @param {SideEffectFnOrObserver} sideEffect Observer object
119+
*
120+
* @return {Function} - unregisterFn
121+
*/
122+
function register<T>(
123+
obs$: SideEffectObservable<T>,
124+
sideEffect?: SideEffectFnOrObserver<T>,
125+
): () => void {
126+
const observer =
127+
typeof sideEffect === 'object'
128+
? {
129+
...sideEffect,
130+
// preserve original logic
131+
error: (e: unknown) => {
132+
sideEffect.error?.(e);
133+
errorHandler?.handleError(e);
134+
},
135+
}
136+
: {
137+
next: sideEffect,
138+
error: (e: unknown) => errorHandler?.handleError(e),
139+
};
140+
const sub = from(obs$).subscribe(observer);
141+
runningEffects.push(sub);
142+
return () => sub.unsubscribe();
143+
}
128144

129-
const effects = {
130-
register,
131-
onDestroy,
132-
};
145+
/**
146+
* Register custom cleanup logic.
147+
*
148+
* @example
149+
*
150+
* /@Component({
151+
* template: `<button name="save" (click)="save()">Save</button>`
152+
* })
153+
* class ListComponent {
154+
* private ef = rxEffects(({onDestroy}) => {
155+
* onDestroy(() => console.log('done'));
156+
* }
157+
* }
158+
*
159+
* @param {Fn} callback onDestroy callback
160+
*
161+
* @return {Fn} unregisterFn
162+
*/
163+
function onDestroy(callback: Fn): Fn {
164+
return destroyRef.onDestroy(callback);
165+
}
133166

134-
setupFn?.(effects);
167+
const effects = {
168+
register,
169+
onDestroy,
170+
};
171+
if (setupFnOrOptions && typeof setupFnOrOptions === 'function') {
172+
setupFnOrOptions(effects);
173+
}
135174

136-
return effects;
175+
return effects;
176+
});
137177
}

0 commit comments

Comments
 (0)