Search code examples
firebase-realtime-databaseionic3angularfire2angular2-observables

Retrieve objects from indexes using angularfire2


I'm trying (with partial success :( ) to retrieve full objects as observables from a collection of indexes of my firebase RTDB using angularfire2 basic methods such as list() and object() in an Ionic app. When retrieving the list of keys for the courses a user has enrolled on I make a new query and get the full data as an observable using the object() method. I get several null in the view when loading the page for the first time but the observables are still alive, so if I make a small change in those objects in the console, the whole object is retrieved and shown in the view without any problem. Am I missing something?

Firebase RTDB root-level nodes

This is my page ts code

import { Component, ViewChild } from '@angular/core';
import { NavController, NavParams, List } from 'ionic-angular';
import { ProfileServiceProvider } from '../../providers/profile-service/profile-service';
import { MomentsFeedPage } from '../moments-feed/moments-feed';
import { CourseServiceProvider } from '../../providers/course-service/course-service';
import { AngularFireDatabase } from 'angularfire2/database';
import { Observable } from 'rxjs/Observable';
/**
 * Generated class for the MomentsPage page.
 *
 * See https://ionicframework.com/docs/components/#navigation for more info on
 * Ionic pages and navigation.
 */

@Component({
  selector: 'page-moments',
  templateUrl: 'moments.html',
})
export class MomentsPage{

  @ViewChild('enrolledList', { read: List }) enrolledList: List;
  public enrolledLis: Observable <{}>;

  constructor(
    public navCtrl: NavController, 
    public navParams: NavParams,
    public courseService: CourseServiceProvider,
    public userProfile: ProfileServiceProvider,
    public afDB: AngularFireDatabase
  ) {

    if(this.userProfile.currentUser) {
      console.log('constructor MomentsPage');
      this.enrolledLis = this.afDB.list('/userEnrollments/'+this.userProfile.currentUser.uid).snapshotChanges()
        .map( res => {
          let enrolled = res;
          let that = this;
          return enrolled.map(key => 
            that.courseService.getCourseDetail(key.key).snapshotChanges()
            .map(snap => 
               ({ key: snap.key, ...snap.payload.val() } )
            )
          )
        }
      );
    }

  }

  goToTopicsFeed(course: any) {
    this.navCtrl.push(MomentsFeedPage, {
      courseId: course.key, courseName: course.name, coursePic: course.coursePic
    });
  }

  ionViewDidLoad() {
    console.log('ionViewDidLoad MomentsPage');
  }

}

And this is the code for the view

<ion-header>
  <ion-navbar>
    <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title>Moments</ion-title>
  </ion-navbar>
</ion-header>
<ion-content no-padding fullscreen parallax-header>
  <div class="header-image" style="background-image:url('./assets/imgs/lists/wishlist-1.jpg')">
    <h1>Moments</h1>
  </div>
  <div class="main-content">
    <ion-list #enrolledList>
      <ion-item-sliding *ngFor="let course of enrolledLis | async" [attr.track]="(course|async)?.degree | courseTrackPipe ">
        <button ion-item (click)="goToTopicsFeed(course)" >
          <ion-thumbnail item-start>
            <img [src]="(course|async)?.coursePic || './assets/imgs/Film-set-greyscale.jpg'" alt="Course profile pic">
          </ion-thumbnail>
          <h2>{{(course|async)?.name}}</h2>
          <h3>{{(course|async)?.degree}}</h3>
          <p>Topics info: #topics {{(course|async)?.topicsCount}} activity...</p>
        </button>
      </ion-item-sliding>
    </ion-list>
  </div>
</ion-content>

Here you can see the behaviour:

Partial processing of the observables (courses) during first call: css class in added so some rules are applied (border in blue or red)

The first object (course observable) was updated in the firebase console and updated without issues in the view


Solution

  • OK. I'll answer to myself: nothing wrong with the code, maybe I tested wrong. Anyway, there was something not so good in the code: returning async Observables may lead to some problems in the (click) action. Those can be solved using a *ngIf="course" block to make sure the object is got during runtime.