Búsqueda de sitios web

typescript - ¿Cómo tratar con propiedades que se inicializan de manera asincrónica en Angular?


Estoy tratando de obtener una AngularFirestoreCollection desde un servicio en Angular. La AngularFirestoreCollection se inicializa asincrónicamente dentro de una suscripción de observable. Cuando intento obtener la colección desde un componente, recibo un error porque está indefinida.

Aquí está el código de mi servicio:

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();
    }
  });

}

Este es el código de mi componente:

 this.myListService.fokosCollection.doc(codeInput).valueChanges().take(1).subscribe(foko => {
   getData(foko)
 });

Intenté crear un Subject que notificara al componente cuando la Colección se inicializara. Esto funcionó cuando navegaba directamente a este componente. Sin embargo, si navegaba a este componente desde otro, la suscripción del subject nunca se llamaba (porque la suscripción no existía cuando el Subject emitía la colección).

Aquí está lo que intenté:

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);
      ...

Y cambié el código del componente a esto:

this.myListService.fokosCollectionSubject.take(1).switchMap(fokosCollection => {
    return fokosCollection.doc(codeInput).valueChanges().take(1);
  }).subscribe(foko => {
    getData(foko)
  });

Puedo intentar obtener la Colección y si lanza un error, suscribirme al Sujeto. Sin embargo, creo que eso no sería un buen código. Entonces, ¿cuál es la mejor práctica para manejar datos asincrónicos en Angular?

Respuesta #1

En tu switchMap no estás devolviendo un valor si el usuario es falso. Deberías devolver algo. Usar el operador filter previene ese caso.

Ya que quieres saber cuándo se inicializa fokosCollection, te sugeriría que lo expongas como un 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());
}

Agregué shareReplay(1) para compartir una única suscripción subyacente y reproducir el último valor a nuevos suscriptores. Puede que esto sea útil o no en tu escenario, pero encuentro que generalmente lo quiero cuando expongo observables que podrían tener múltiples suscripciones.

Entonces, en tu componente:

this.myListService.fokosCollection.switchMap(x => x.doc(codeInput).valueChanges()).take(1).subscribe(foko => {
   getData(foko)
 });