Search code examples
angularjstypescriptfirebasefirebase-realtime-databaseangularfire2

How to display firebase multiple path information in a single Angular page?


I have a simple database like this:

{
  "groups": {
    "123": {
      "name": "developers",
      "users": {
        "1": true
      },
      "users_count": 1
    }
  },
  "events": {
    "456": {
      "name": "Developers conference",
      "users": {
        "1": true
      },
      "users_count": 1
    }
  },
  "users": {
    "1": {
      "name": "Jon",
      "groups": {
        "123": true
      },
      "events": {
        "456": true
      }
    }
  }
}

In the user's homepage, I want to display all the information about the groups and events, such as their names. So, in the Homepage class, i'm doing this:

After getting the user's information, I check if it has a group or event. If so, I get the key of each one, subscribe() to the group or event using the key and add the data to an array after it is loaded.

export class HomePage {

  user: FirebaseObjectObservable<any>;
  groupsKey: Array<any>;
  groups: Array<any> = [];

  eventsKey: Array<any>;
  events: Array<any> = [];

  constructor(private _auth: AuthService, public afDB: AngularFireDatabase) {

    this.user = this.afDB.object(`/users/${this._auth.uid}`);
    this.user.subscribe(user =>{

      if(user.groups){
        this.groupsKey = Object.keys(user.groups); 
        this.groupsKey.forEach(key => {
          let groupObservable = this.afDB.object(`/groups/${key}`);
          groupObservable.subscribe(group => {
            this.groups.push(group);
          })
        })
      }

      if(user.events){
        this.eventsKey = Object.keys(user.events);
        this.eventsKey.forEach(key => {
          let eventObservable = this.afDB.object(`/events/${key}`);
          eventObservable.subscribe(event => {
            this.events.push(event);
          })
        })
      }

    })
  }
}

In the HTML part:

<div *ngFor="let item of groups">
  {{item.name}}: {{item.users_count}} users
</div>

This approach works at first sight, but when some update is made in a group or event, a duplicate is pushed in the array, going back to normal when the page is reloaded.

What can be a better implementation in this case? I know that I should remove the arrays and use the async pipe, but couldn't figure out how to do this.


Solution

  • If anyone stumble upon with this answer with the same question I had, here's my solution:

    I got rid of static arrays and used only Observables and its operators.

    export class HomePage {
    
      user: FirebaseObjectObservable<any>;
      groups: Observable<Array<{}>>;
      events: Observable<Array<{}>>;
    
      constructor(private _auth: AuthService, public afDB: AngularFireDatabase) {
    
        this.user = this.afDB.object(`/users/${this._auth.uid}`);
        this.user.subscribe(user =>{
    
          if(user.groups){
            this.groups = Observable.of(user.groups)
            .map(obj => {
              let arr = [];
              Object.keys(obj).forEach((key) =>{
               //get an Observable containing the info for each key in user.groups object.
                arr.push(this.afDB.object(`groups/${key}`)); 
              })
              //zip() all Observables in the array
              let zip = Observable.zip(...arr);
    
              //return the emitted values (will return an Observable)
              return zip;
            })
            //use switchMap() to flatten the Observables
            .switchMap(val => val)
          }
    
          if(user.events){
            this.events = Observable.of(user.events)
            .map(obj => {
              let arr = [];
              Object.keys(obj).forEach((key) =>{
                arr.push(this.afDB.object(`events/${key}`)); 
              })
              let zip = Observable.zip(...arr);
              return zip;
            })
            .switchMap(val => val)
          }
    
        })
      }
    }