-1

I am trying to retrieve data from Firebase within a service and use it in a component in order to display the data on my template. I'm using Angularfire2 & Firebase.

Service code:

export class MaterialDataFirebaseService {

  constructor(private db:AngularFireDatabase) {

  }

  getData(group, endKey?) {
    let query = {
      orderByKey: true,
      limitToFirst: group
    };

    if(endKey) query['startAt'] = endKey;

    // tried the two commented blocks below, but gave errors

    // return this.db.list('/', {
    //   query
    // });

    // return this.db.list('/', ref => {
    //   let q = ref.limitTolast(25).orderByKey(true);
    //   return q;
    // });

    this.db.list('/', ref => 
    ref.orderByKey().limitToFirst(group))
    .valueChanges()
    .subscribe(materialData => {
      return materialData;
    });
  }
}

Component code:

export class LibraryMaterialCardComponent implements OnInit {
  materials: Material[];
  firebaseData = new BehaviorSubject([]);

  group = 8;
  endKey = '';
  finished = false;

  constructor(private dataService:DataService, private _materialDataFirebaseService: MaterialDataFirebaseService) {

  }

  ngOnInit() {
    this.dataService.getMaterialData().subscribe((materials) => {
      this.materials = materials;
    });

   this.getFirebaseData();
  }

  onScroll() {
    this.getFirebaseData();
  }

  private getFirebaseData(key?) {
    if(this.finished) return;

    this._materialDataFirebaseService.getData(this.group + 1, this.endKey)
      .do(firebaseData => {
        this.endKey = _.last(firebaseData)['$key']
        const newData = _.slice(firebaseData, 0, this.group)
        const currentData = this.firebaseData.getValue;

        if(this.endKey == _.last(newData)['$key']) {
          this.finished = true;
        }

        this.firebaseData.next(_.concat(currentData, newData));
      })
      .take(1)
      .subscribe();
  }

}


interface Material{
  id: number,
  name: string,
  article_id: string,
  tags: object,
  state: string,
  gallery_image: string
}

I did make sure that I imported everything properly and everything seems to work except I'm getting this Property 'do' does not exist on type 'void'. in the component class.

The main idea of what I want to do is retrieving the data from firebase one batch at a time and load more on scroll event, to achieve this so called "Infinite Scroll" to reduce server load.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807

1 Answers1

0

Firebase APIs are asynchronous, which means they return immediately when invoked, and the data you're looking for becomes available later in a callback. In the code you showed, your callback is trying to return materialData from within a closure, which is not possible. Put some log statements in your code, and you'll see that the getData function actually returns (undefined) before the callback is invoked.

Bottom line is that you can't make an asynchronous call synchronous, and you would never want to, because that would block your code and make your app janky. You'll need to deal with Firebase APIs asynchronously as they were intended.

To learn more about why the Firebase APIs are asynchronous and what to expect from them, read this blog.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • I do understand the asynchronous & synchronous concepts. Its just not clear to me how to go on about it in my case, returning data from firebase asynchronously with the parameters and methods I need to use to achieve the Infinite Scroll. Attempted many times but failed. – Ahmet Ömer Mar 03 '18 at 23:17
  • Then I would suggest asking another question that focuses on the problem of infinite scrolling, rather than the issue of why you apparently can't call a method on a void/undefined thing (and that you're trying to return a value in the wrong scope). – Doug Stevenson Mar 03 '18 at 23:32
  • The issue here isn't solely on how to achieve Infinite Scroll but how to make a proper --asynchronous-- call to firebase and retrieve data so that I can properly use the returned data in my component, the rest is done already. For sure if I was going to ask another question people would just tell me I can't code this and that and not give a clear solution instead. – Ahmet Ömer Mar 03 '18 at 23:49
  • Just have your method accept a callback that you invoke from the Firestore callback, or return a promise that the caller can use to find out when the database is available. You're just wrapping an API, so make your function look like that API. – Doug Stevenson Mar 03 '18 at 23:53
  • Just to be clear here: your *actual* question is this: "How do I make a function return data from an asynchronous function?" If you google something along those lines, including the term "javascript", you'll find this as the top hit: https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call – Doug Stevenson Mar 03 '18 at 23:56