4
4
Injector ,
5
5
NgZone ,
6
6
PendingTasks ,
7
- inject
7
+ inject ,
8
+ isDevMode ,
9
+ runInInjectionContext
8
10
} from '@angular/core' ;
9
11
import { pendingUntilEvent } from '@angular/core/rxjs-interop' ;
10
12
import {
@@ -76,31 +78,71 @@ function getSchedulers() {
76
78
return inject ( ɵAngularFireSchedulers ) ;
77
79
}
78
80
81
+ var alreadyWarned = false ;
82
+ function warnOutsideInjectionContext ( original : any , operation : string ) {
83
+ if ( isDevMode ( ) ) {
84
+ console . warn ( `Firebase API called outside injection context: ${ operation } (${ original . name } )` ) ;
85
+ if ( ! alreadyWarned ) {
86
+ alreadyWarned = true ;
87
+ console . error ( "Calling Firebase APIs outside of an Injection context may destabilize your application leading to subtle change-detection and hydration bugs. Find more at https://github.com/angular/angularfire/blob/main/docs/zones.md" ) ;
88
+ }
89
+ }
90
+ }
91
+
79
92
function runOutsideAngular < T > ( fn : ( ...args : any [ ] ) => T ) : T {
80
- return inject ( NgZone ) . runOutsideAngular ( ( ) => fn ( ) ) ;
93
+ let ngZone : NgZone | undefined ;
94
+ try {
95
+ ngZone = inject ( NgZone ) ;
96
+ } catch ( e ) {
97
+ warnOutsideInjectionContext ( fn , "runOutsideAngular" ) ;
98
+ }
99
+ if ( ! ngZone ) { return fn ( ) ; }
100
+ return ngZone . runOutsideAngular ( ( ) => fn ( ) ) ;
81
101
}
82
102
83
103
function run < T > ( fn : ( ...args : any [ ] ) => T ) : T {
84
- return inject ( NgZone ) . run ( ( ) => fn ( ) ) ;
104
+ let ngZone : NgZone | undefined ;
105
+ try {
106
+ ngZone = inject ( NgZone ) ;
107
+ } catch ( e ) {
108
+ warnOutsideInjectionContext ( fn , "run" ) ;
109
+ }
110
+ if ( ! ngZone ) { return fn ( ) ; }
111
+ return ngZone . run ( ( ) => fn ( ) ) ;
85
112
}
86
113
87
114
export function observeOutsideAngular < T > ( obs$ : Observable < T > ) : Observable < T > {
88
- return obs$ . pipe ( observeOn ( getSchedulers ( ) . outsideAngular ) ) ;
115
+ let schedulers : ɵAngularFireSchedulers | undefined ;
116
+ try {
117
+ schedulers = getSchedulers ( ) ;
118
+ } catch ( e ) {
119
+ warnOutsideInjectionContext ( obs$ , "observeOutsideAngular" ) ;
120
+ }
121
+ if ( ! schedulers ) { return obs$ ; }
122
+ return obs$ . pipe ( observeOn ( schedulers . outsideAngular ) ) ;
89
123
}
90
124
91
125
export function observeInsideAngular < T > ( obs$ : Observable < T > ) : Observable < T > {
92
- return obs$ . pipe ( observeOn ( getSchedulers ( ) . insideAngular ) ) ;
126
+ let schedulers : ɵAngularFireSchedulers | undefined ;
127
+ try {
128
+ schedulers = getSchedulers ( ) ;
129
+ } catch ( e ) {
130
+ warnOutsideInjectionContext ( obs$ , "observeInsideAngular" ) ;
131
+ }
132
+ if ( ! schedulers ) { return obs$ ; }
133
+ return obs$ . pipe ( observeOn ( schedulers . insideAngular ) ) ;
93
134
}
94
135
95
136
const zoneWrapFn = (
96
137
it : ( ...args : any [ ] ) => any ,
97
- taskDone : VoidFunction | undefined
138
+ taskDone : VoidFunction | undefined ,
139
+ injector : Injector ,
98
140
) => {
99
141
return ( ...args : any [ ] ) => {
100
142
if ( taskDone ) {
101
143
setTimeout ( taskDone , 0 ) ;
102
144
}
103
- return run ( ( ) => it . apply ( this , args ) ) ;
145
+ return runInInjectionContext ( injector , ( ) => run ( ( ) => it . apply ( this , args ) ) ) ;
104
146
} ;
105
147
} ;
106
148
@@ -117,6 +159,7 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
117
159
pendingTasks = inject ( PendingTasks ) ;
118
160
injector = inject ( Injector ) ;
119
161
} catch ( e ) {
162
+ warnOutsideInjectionContext ( it , "ɵzoneWrap" ) ;
120
163
return ( it as any ) . apply ( this , _arguments ) ;
121
164
}
122
165
// if this is a callback function, e.g, onSnapshot, we should create a pending task and complete it
@@ -127,7 +170,7 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
127
170
taskDone ||= run ( ( ) => pendingTasks . add ( ) ) ;
128
171
}
129
172
// TODO create a microtask to track callback functions
130
- _arguments [ i ] = zoneWrapFn ( _arguments [ i ] , taskDone ) ;
173
+ _arguments [ i ] = zoneWrapFn ( _arguments [ i ] , taskDone , injector ) ;
131
174
}
132
175
}
133
176
const ret = runOutsideAngular ( ( ) => ( it as any ) . apply ( this , _arguments ) ) ;
@@ -153,8 +196,8 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
153
196
( ) =>
154
197
new Promise ( ( resolve , reject ) => {
155
198
pendingTasks . run ( ( ) => ret ) . then (
156
- ( it ) => run ( ( ) => resolve ( it ) ) ,
157
- ( reason ) => run ( ( ) => reject ( reason ) )
199
+ ( it ) => runInInjectionContext ( injector , ( ) => run ( ( ) => resolve ( it ) ) ) ,
200
+ ( reason ) => runInInjectionContext ( injector , ( ) => run ( ( ) => reject ( reason ) ) )
158
201
) ;
159
202
} )
160
203
) ;
0 commit comments