I am trying to get an AngularFirestoreCollection from a service in angular. The AngularFirestoreCollection is initialized asynchronously inside an observable subscription. When I try to get the collection from a component, I get an error because it is undefined.
Here is the code for my service:
fokosCollection: AngularFirestoreCollection<Foko>;
myFokos: Observable<Foko[]>;
constructor(auth: AuthService, public afs: AngularFirestore) {
this.myFokos = auth.user$.switchMap(user => {
if (user) {
this.fokosCollection = afs.collection<Foko>('users/' + user.uid + '/fokos');
return this.fokosCollection.valueChanges();
}
});
}
This is the code of my component:
this.myListService.fokosCollection.doc(codeInput).valueChanges().take(1).subscribe(foko => {
getData(foko)
});
I tried make a Subject that notified the component when the Collection was initialized. This worked when I navigated directly to this component. However, if I navigated to this component from another, the subject subscription was never called (because the subscription did not exist when the Subject emitted the collection).
Here is what I tried:
fokosCollection: AngularFirestoreCollection<Foko>;
fokosCollectionSubject = new Subject<AngularFirestoreCollection<Foko>>();
myFokos: Observable<Foko[]>;
constructor(auth: AuthService, public afs: AngularFirestore) {
this.myFokos = auth.user$.switchMap(user => {
if (user) {
this.fokosCollection = afs.collection<Foko>('users/' + user.uid + '/fokos');
this.fokosCollectionSubject.next(this.fokosCollection);
...
And I changed the code of the component to this:
this.myListService.fokosCollectionSubject.take(1).switchMap(fokosCollection => {
return fokosCollection.doc(codeInput).valueChanges().take(1);
}).subscribe(foko => {
getData(foko)
});
I can try to get the Collection and if it throws an error, subscribe to the Subject. However, I think that would not be good code. So, what is the best practice to deak with asynchronous data in Angular?
In your switchMap you are not returning a value if user is falsy. You should return something. Using the filter operator prevents that case.
Since you want to know when fokosCollection is initialized I would suggest that you expose it as an observable.
fokosCollection: Observable<AngularFirestoreCollection<Foko>>;
myFokos: Observable<Foko[]>;
constructor(auth: AuthService, public afs: AngularFirestore) {
this.fokosCollection = auth.user$
.filter(x => x != null)
.map(user => afs.collection<Foko>('users/' + user.uid + '/fokos'))
.shareReplay(1);
this.myFokos = this.fokosCollection.switchMap(x => x.valueChanges());
}
I added shareReplay(1) to share a single underlying subscription and to replay the last value to new subscribers. You may or may not want this in your scenario but I find that I generally want it when exposing observables that could have multiple subscriptions.
Then in your component:
this.myListService.fokosCollection.switchMap(x => x.doc(codeInput).valueChanges()).take(1).subscribe(foko => {
getData(foko)
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With