Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ionic 3 - Update Observable with Asynchronous Data

For a social media app, I have a collection of feed objects referenced by their IDs using AngularFire 2. Once each of these IDs has its relevant data pulled from the database that stores the actual feed objects, I wish to update the feedCards Observable object with this information so I can asynchronously display a collection of feed objects in my HTML. It's a pretty confusing chain of events, so let me summarize it for you.

Step-by-step Approach

  1. displayFeed() is invoked right before the NavController loads the feed component on the Main page.
  2. displayFeed() gets the twiner item, which is essentially a user profile item, and then stores the user profile in the userProfile variable.
  3. Once the user profile is loaded, the global feedCards Observable is set equal to loadFeed(), which returns an Observable array.
  4. loadFeed() uses the id value of the userProfile global object to load the list of feed references stored in the user profile.
  5. This snapshot is then subscribed to and the local feed variable is set equal to the result list of feed references.
  6. loadFeed returns an Observable object in which the feed reference list is mapped by the data each feed reference contains.
  7. pullFeedData(number) takes in a reference to a feed object and returns an observable with the associated feed data.

What Works

  • alert(JSON.stringify(feedData)); alerts the correct feed object

  • Basically everything else.

What Doesn't Work

  • feed.map(... does not update the feed array because pullFeedData(number) pulls and returns the feedData asynchronously. Thus, alert(JSON.stringify(data)); in displayFeed() alerts [null].

Code

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>

Summary

feed.map does not update feed with feed objects because the new data is being pulled asynchronously. I need a fix for this.

Thank you!

like image 936
Anthony Krivonos Avatar asked May 28 '17 16:05

Anthony Krivonos


1 Answers

A subscribed observable does not return a value. It returns a Subscription object which you can use to unsubscribe later.

You can use switchMap to switch from first observable to the next during the call and return the values. Also you can use forkJoin which will call an array of observables and wait till the array of values is returned in subscription. First declare feed class variable as an Observable.

feed: Observable<any>;

Then in your loadFeed():Observable<any[]>

return this.feed.switchMap((snapshots) => {
             let observableArray = [];
             snapshots.forEach(snapshot =>{
                 observableArray.push(this.pullFeedData(snapshot.val()));
             });
             return Observable.forkJoin(observableArray)
        })
like image 134
Suraj Rao Avatar answered Nov 16 '22 17:11

Suraj Rao