Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with properties that are initilaized asynchronously in Angular?

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?

like image 310
Sondre Sørbye Avatar asked Dec 18 '25 22:12

Sondre Sørbye


1 Answers

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)
 });
like image 183
bygrace Avatar answered Dec 21 '25 13:12

bygrace



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!