javascript - Ionic 3 - Oppdater Observable med asynkrone data


For en sosial medieapp har jeg en samling av feed-objekter referert med deres ID-er ved bruk av AngularFire 2. Når hver av disse ID-ene har fått hentet relevant data fra databasen som lagrer de faktiske feed-objektene, ønsker jeg å oppdatere feedCards Observable-objektet med denne informasjonen slik at jeg kan vise en samling av feed-objekter asynkront i HTML-en min. Det er en ganske forvirrende kjede av hendelser, så la meg oppsummere det for deg.

Trinnvis tilnærming

  1. displayFeed() blir kalt rett før NavController laster feed-komponenten på Main-siden.
  2. displayFeed() henter twiner-elementet, som i hovedsak er et brukerprofil-element, og lagrer deretter brukerprofilen i userProfile-variabelen.
  3. Når brukerprofilen er lastet, settes den globale feedCards Observable lik loadFeed(), som returnerer et Observable-array.
  4. loadFeed() bruker id-verdien til userProfile-globale objektet for å laste listen over feed-referanser lagret i brukerprofilen.
  5. Dette øyeblikksbildet blir deretter abonnert på, og den lokale feed-variabelen settes lik resultatlisten over feed-referanser.
  6. loadFeed returnerer et Observable-objekt der feed-referanselisten er kartlagt etter dataene hver feed-referanse inneholder.
  7. pullFeedData(number) tar inn en referanse til et feed-objekt og returnerer en observerbar med de tilknyttede feed-dataene.

Hva som fungerer

  • alert(JSON.stringify(feedData)); viser den riktige feed-objektet

  • Egentlig alt annet.

Hva fungerer ikke

  • feed.map(... oppdaterer ikke feed-arrayet fordi pullFeedData(number) henter og returnerer feedData asynkront. Dermed vil alert(JSON.stringify(data)); i displayFeed() vise [null].

Kode

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>

Sammendrag

feed.map oppdaterer ikke feed med feed-objekter fordi de nye dataene hentes asynkront. Jeg trenger en løsning på dette.

Takk!

Svar #1

Et abonnert observable returnerer ikke en verdi. Det returnerer et Subscription-objekt som du kan bruke til å avslutte abonnementet senere.

Du kan bruke switchMap for å bytte fra den første observable til den neste under kallingen og returnere verdiene. Du kan også bruke forkJoin som vil kalle et array av observables og vente til arrayet av verdier returneres i abonnementet. Først deklarer feed som en klassevariabel som en Observable.

feed: Observable<any>;

Deretter i din loadFeed():Observable<any[]>

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