dmuensterer
dmuensterer

Reputation: 1885

Paginating observable Array

I want to display list of documents that are fetched from Firestore. I'd like to display 5 documents by default and show a "Load more" button that will fetch another 5 documents when clicked.

With a static list, I'd do it like this, rather easy:

loadMoreInvoices() {
  //Get number of last item
  var lastItemInvoiceNumber = (this.invoices[this.invoices.length-1] as any).invoice_number;

  this.afs.collection('clients').doc(uid).collection('invoices', ref => ref.orderBy('invoice_number').startAt(lastItemInvoiceNumber).limit(5+1)).get().then(snap => {

    //Remove first elem from array as it is a duplicate
    snap.shift()
    //Add all loaded invoices to array
    snap.forEach(item => {
      this.invoices.push(item)
    })

    //Check if there are any more invoices to be loaded and set status accordingly
    if (snap.length < this.moreInvoicesToBeLoaded) {
      this.noMoreInvoices = true;
    } else {
      this.noMoreInvoices = false;
    }
  });
}

ngOnInit() {
    this.afs.collection('clients').doc(uid).collection('invoices', ref => ref.orderBy('invoice_number').limit(invoicesToBeLoaded)).get().then(snap => {
        if (invoices.length < this.invoicesToBeLoaded) {
            //Display "Load more" only if false
            this.noMoreInvoices = true;
        }
        this.invoices = invoices;
        this.loaded = true;
    })
}

How can I get the same behaviour using Observables instead of static data? The way I did it above would result in a corruption of the list after this.invoices changes due to the Observable.

Upvotes: 3

Views: 373

Answers (1)

Sergey
Sergey

Reputation: 7692

Adding some information increnemntally is possible with help of scan operator which allows you to use accumulator and return a new value which will be passed to the consumer and will serve as an accumulator for next observable emit.

You could make something like this

source$ = this.page$.pipe(
  switchMap(page => this.getList(page)),
  // here the magic goes. We initialize scan with "[]" seed (otherwise only second
  // emit will be passed further as first one would be taken for the seed for accumulator)
  // and use concat which returns new array of concatenated "acc" and "vall"
  scan((acc, val) => acc.concat(val), [])
)

Then you simply use source$ | async in template and you have your data fetched and updated incrementally (aka infinite scroll).

this.page$ is an observable for paging to make a new call to the remote resource.

Upvotes: 5

Related Questions