Angular:
9.1.4
Firebase:
7.14.2
AngularFire:
6.0.0
I have a UsersService which fetches users over https callable functions.
Subscribing to the observable returned breaks the change detection in my component.
Changing the changeDetection to ChangeDetectionStrategy.OnPush and explicitly calling ChangeDetectorRef.markForCheck() and ApplicationRef.tick() or running the subscribe block in an NgZone fixes the problem.
Steps to set up and reproduce
class MyService {
constructor(protected firebaseFunctions: AngularFireFunctions) {}
fetch() {
const f = this.firebaseFunctions.httpsCallable("myFunc");
return f({});
}
}
class MyComponent implements OnInit {
res: any;
constructor(private service: MyService, private zone: NgZone) {}
ngOnInit() {
this.service.fetch().subscribe((res) => {
this.res = res;
// This works:
// this.zone.run(() => {
// this.res = res;
// });
});
}
}
Shouldnt break change detection or explicitly state how to work around it in docs.
Does not work.
Same issue over here..
same here
Thanks to the workaround in #2444 I was able to fixe the auth guard.
For functions ive just copied the original source and changed outsideAngular into insideAngular and overrode the DI token.
import {Inject, Injectable, NgZone, Optional} from '@angular/core';
import {from, Observable, of} from 'rxjs';
import {map, observeOn, shareReplay, switchMap, tap} from 'rxjs/operators';
import {
FIREBASE_APP_NAME,
FIREBASE_OPTIONS,
FirebaseAppConfig,
FirebaseOptions,
ɵAngularFireSchedulers,
ɵfirebaseAppFactory,
ɵlazySDKProxy
} from '@angular/fire';
import {ORIGIN, REGION} from '@angular/fire/functions';
@Injectable({
providedIn: 'any'
})
export class FirebaseFunctions {
public readonly httpsCallable: <T=any, R=any>(name: string) => (data: T) => Observable<R>
constructor(
@Inject(FIREBASE_OPTIONS) options:FirebaseOptions,
@Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig:string|FirebaseAppConfig|null|undefined,
zone: NgZone,
@Optional() @Inject(REGION) region:string|null,
@Optional() @Inject(ORIGIN) origin:string|null
) {
const schedulers = new ɵAngularFireSchedulers(zone);
const functions = of(undefined).pipe(
observeOn(schedulers.insideAngular),
switchMap(() => import('firebase/functions')),
map(() => ɵfirebaseAppFactory(options, zone, nameOrConfig)),
map(app => app.functions(region || undefined)),
tap(functions => {
if (origin) { functions.useFunctionsEmulator(origin) }
}),
shareReplay({ bufferSize: 1, refCount: false }),
);
this.httpsCallable = <T=any, R=any>(name: string) =>
(data: T) => from(functions).pipe(
observeOn(schedulers.insideAngular),
switchMap(functions => functions.httpsCallable(name)(data)),
map(r => r.data as R)
)
return ɵlazySDKProxy(this, functions, zone);
}
}
And in my module:
...
providers: [
{
provide: AngularFireFunctions,
useClass: FirebaseFunctions
}
],
...
I solved this problem with this code
```typescript
Return from(f({}).toPromise())
````
There's still a team working on it? The latest updates are full of bugs like this.
Saw somewhere else that there is already a fix for future 6.0.3
Thanks to the workaround in #2444 I was able to fixe the auth guard.
For functions ive just copied the original source and changed
outsideAngularintoinsideAngularand overrode the DI token.import {Inject, Injectable, NgZone, Optional} from '@angular/core'; import {from, Observable, of} from 'rxjs'; import {map, observeOn, shareReplay, switchMap, tap} from 'rxjs/operators'; import { FIREBASE_APP_NAME, FIREBASE_OPTIONS, FirebaseAppConfig, FirebaseOptions, ɵAngularFireSchedulers, ɵfirebaseAppFactory, ɵlazySDKProxy } from '@angular/fire'; import {ORIGIN, REGION} from '@angular/fire/functions'; @Injectable({ providedIn: 'any' }) export class FirebaseFunctions { public readonly httpsCallable: <T=any, R=any>(name: string) => (data: T) => Observable<R> constructor( @Inject(FIREBASE_OPTIONS) options:FirebaseOptions, @Optional() @Inject(FIREBASE_APP_NAME) nameOrConfig:string|FirebaseAppConfig|null|undefined, zone: NgZone, @Optional() @Inject(REGION) region:string|null, @Optional() @Inject(ORIGIN) origin:string|null ) { const schedulers = new ɵAngularFireSchedulers(zone); const functions = of(undefined).pipe( observeOn(schedulers.insideAngular), switchMap(() => import('firebase/functions')), map(() => ɵfirebaseAppFactory(options, zone, nameOrConfig)), map(app => app.functions(region || undefined)), tap(functions => { if (origin) { functions.useFunctionsEmulator(origin) } }), shareReplay({ bufferSize: 1, refCount: false }), ); this.httpsCallable = <T=any, R=any>(name: string) => (data: T) => from(functions).pipe( observeOn(schedulers.insideAngular), switchMap(functions => functions.httpsCallable(name)(data)), map(r => r.data as R) ) return ɵlazySDKProxy(this, functions, zone); } }And in my module:
... providers: [ { provide: AngularFireFunctions, useClass: FirebaseFunctions } ], ...
Great, this worked for me! I had to make a wrapper service in root module though - didn't work directly in lazyloaded child module.
I solved this problem with this code
Return from(f({}).toPromise())
Could only get this to work "once" for each promise, and it's a mess if you're combining different observables.
Most helpful comment
I solved this problem with this code
```typescript
Return from(f({}).toPromise())
````