@@ -3,24 +3,43 @@ import {
3
3
DestroyRef ,
4
4
ErrorHandler ,
5
5
inject ,
6
+ Injector ,
7
+ runInInjectionContext ,
6
8
} from '@angular/core' ;
7
9
import { from , Subscription } from 'rxjs' ;
8
10
import { SideEffectFnOrObserver , SideEffectObservable } from './types' ;
9
11
10
12
interface RxEffects {
11
13
register < T > (
12
14
observable : SideEffectObservable < T > ,
13
- sideEffectOrObserver ?: SideEffectFnOrObserver < T >
15
+ sideEffectOrObserver ?: SideEffectFnOrObserver < T > ,
14
16
) : Fn ;
15
17
onDestroy : ( fn : Fn ) => Fn ;
16
18
}
17
19
18
20
type Fn = ( ) => void ;
19
21
20
22
export type RxEffectsSetupFn = (
21
- cfg : Pick < RxEffects , 'register' | 'onDestroy' >
23
+ cfg : Pick < RxEffects , 'register' | 'onDestroy' > ,
22
24
) => void ;
23
25
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
+
24
43
/**
25
44
* @description
26
45
* Functional way to setup observable based side effects with RxEffects.
@@ -55,83 +74,104 @@ export type RxEffectsSetupFn = (
55
74
* @docsPage RxEffects
56
75
*
57
76
*/
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 ) ;
105
91
}
106
92
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
+ }
128
144
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
+ }
133
166
134
- setupFn ?.( effects ) ;
167
+ const effects = {
168
+ register,
169
+ onDestroy,
170
+ } ;
171
+ if ( setupFnOrOptions && typeof setupFnOrOptions === 'function' ) {
172
+ setupFnOrOptions ( effects ) ;
173
+ }
135
174
136
- return effects ;
175
+ return effects ;
176
+ } ) ;
137
177
}
0 commit comments