44 Injector ,
55 NgZone ,
66 PendingTasks ,
7- inject
7+ inject ,
8+ isDevMode ,
9+ runInInjectionContext
810} from '@angular/core' ;
911import { pendingUntilEvent } from '@angular/core/rxjs-interop' ;
1012import {
@@ -76,31 +78,71 @@ function getSchedulers() {
7678 return inject ( ɵAngularFireSchedulers ) ;
7779}
7880
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+
7992function 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 ( ) ) ;
81101}
82102
83103function 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 ( ) ) ;
85112}
86113
87114export 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 ) ) ;
89123}
90124
91125export 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 ) ) ;
93134}
94135
95136const zoneWrapFn = (
96137 it : ( ...args : any [ ] ) => any ,
97- taskDone : VoidFunction | undefined
138+ taskDone : VoidFunction | undefined ,
139+ injector : Injector ,
98140) => {
99141 return ( ...args : any [ ] ) => {
100142 if ( taskDone ) {
101143 setTimeout ( taskDone , 0 ) ;
102144 }
103- return run ( ( ) => it . apply ( this , args ) ) ;
145+ return runInInjectionContext ( injector , ( ) => run ( ( ) => it . apply ( this , args ) ) ) ;
104146 } ;
105147} ;
106148
@@ -117,6 +159,7 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
117159 pendingTasks = inject ( PendingTasks ) ;
118160 injector = inject ( Injector ) ;
119161 } catch ( e ) {
162+ warnOutsideInjectionContext ( it , "ɵzoneWrap" ) ;
120163 return ( it as any ) . apply ( this , _arguments ) ;
121164 }
122165 // 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 => {
127170 taskDone ||= run ( ( ) => pendingTasks . add ( ) ) ;
128171 }
129172 // TODO create a microtask to track callback functions
130- _arguments [ i ] = zoneWrapFn ( _arguments [ i ] , taskDone ) ;
173+ _arguments [ i ] = zoneWrapFn ( _arguments [ i ] , taskDone , injector ) ;
131174 }
132175 }
133176 const ret = runOutsideAngular ( ( ) => ( it as any ) . apply ( this , _arguments ) ) ;
@@ -153,8 +196,8 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
153196 ( ) =>
154197 new Promise ( ( resolve , reject ) => {
155198 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 ) ) )
158201 ) ;
159202 } )
160203 ) ;
0 commit comments