Búsqueda de sitios web

javascript - Ionic 3 - Actualizar Observable con Datos Asíncronos


Para una aplicación de redes sociales, tengo una colección de objetos de feed referenciados por sus IDs usando AngularFire 2. Una vez que cada uno de estos IDs tiene sus datos relevantes extraídos de la base de datos que almacena los objetos de feed reales, deseo actualizar el objeto Observable feedCards con esta información para poder mostrar asincrónicamente una colección de objetos de feed en mi HTML. Es una cadena de eventos bastante confusa, así que permíteme resumírtelo.

Enfoque paso a paso

  1. displayFeed() se invoca justo antes de que el NavController cargue el componente feed en la página Main.
  2. displayFeed() obtiene el elemento twiner, que es esencialmente un elemento de perfil de usuario, y luego almacena el perfil de usuario en la variable userProfile.
  3. Una vez que se carga el perfil del usuario, el Observable global feedCards se establece igual a loadFeed(), que devuelve un array Observable.
  4. loadFeed() utiliza el valor id del objeto global userProfile para cargar la lista de referencias del feed almacenadas en el perfil del usuario.
  5. Luego se suscribe a esta instantánea y la variable local feed se establece igual a la lista de resultados de referencias de feed.
  6. loadFeed devuelve un objeto Observable en el cual la lista de referencias feed se mapea según los datos que contiene cada referencia de feed.
  7. pullFeedData(number) recibe una referencia a un objeto feed y devuelve un observable con los datos asociados del feed.

Lo que funciona

  • alert(JSON.stringify(feedData)); muestra una alerta con el objeto feed correcto

  • Básicamente todo lo demás.

Lo que no funciona

  • El feed.map(... no actualiza el array feed porque pullFeedData(number) obtiene y devuelve el feedData de forma asincrónica. Por lo tanto, alert(JSON.stringify(data)); en displayFeed() muestra una alerta con [null].

Código

feed.ts
   userProfile:any;
   feed: FirebaseListObservable<any>;
   feedData: FirebaseObjectObservable<any>;

   feedCards: Observable<any[]>;

   constructor(public db: AngularFireDatabase, public nativeStorage: NativeStorage) {}

   displayFeed():void {
        this.nativeStorage.getItem('twiner').then((res) => {
              this.userProfile = res;
              this.feedCards = this.loadFeed();
              this.feedCards.subscribe((data)=>{
                    alert(JSON.stringify(data));
              })
        });
  }

  loadFeed():Observable<any[]> {
        var feed;
        this.feed = this.db.list('/twiners/' + this.userProfile.id + '/feed', { preserveSnapshot: true });
        this.feed.subscribe((snapshots)=>{feed = snapshots});
        return Observable.of(feed.map((snapshot) => {
              this.pullFeedData(snapshot.val()).subscribe((feedData)=>{
                    alert(JSON.stringify(feedData));
                    return feedData;
              });
        })).delay(1000);
  }

  pullFeedData(twine:number):any {
        return this.db.object('/twines/' + twine, { preserveSnapshot: true });
  }
feed.html
<ion-content fullscreen scroll="true" overflow-scroll="true">
      <ion-card *ngIf="feedCards | async">feedCards exist</ion-card>
      <twine-feed-card *ngFor="let feedCard of feedCards | async"
            [data]="feedCard"
      ></twine-feed-card>
</ion-content>

Resumen

feed.map no actualiza feed con los objetos feed porque los nuevos datos se están obteniendo de forma asincrónica. Necesito una solución para esto.

¡Gracias!

Respuesta #1

Un observable suscrito no devuelve un valor. Devuelve un objeto Subscription que puedes usar para cancelar la suscripción más tarde.

Puedes usar switchMap para cambiar del primer observable al siguiente durante la llamada y devolver los valores. También puedes usar forkJoin, que llamará a un arreglo de observables y esperará hasta que se devuelva el arreglo de valores en la suscripción. Primero declara la variable de clase feed como un Observable.

feed: Observable<any>;

Entonces en tu loadFeed():Observable<any[]>

return this.feed.switchMap((snapshots) => {
             let observableArray = [];
             snapshots.forEach(snapshot =>{
                 observableArray.push(this.pullFeedData(snapshot.val()));
             });
             return Observable.forkJoin(observableArray)
        })