Ionic AngularFirebase无限滚动分页服务

时间:2019-11-17 01:04:46

标签: angular google-cloud-firestore ionic4

我正在尝试在我的离子型角度应用程序中实现无限滚动。对此我是新手,我需要一些专家意见。问题是more()函数在第一次滚动事件发生时起作用,但是在尝试获取第二组文档时会陷入无限循环。这是代码,我包括了组件和html的图片以及完整的分页服务。

Component.ts:

  ngOnInit() {

      this.page.init('Documents', 'Name', { reverse: false, prepend: false })

  scrollHandler(e) {
      if (e === 'bottom') {
        this.page.more()
      }
     }

模板html:

<ion-infinite-scroll threshold="100px" (ionInfinite)="this.page.more($event)">
  <ion-infinite-scroll-content
    loadingSpinner="bubbles"
    loadingText="Loading more data...">
  </ion-infinite-scroll-content>
</ion-infinite-scroll>

pagination-service.ts:

import { Injectable } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection } from  '@angular/fire/firestore';

import { BehaviorSubject } from 'rxjs';
import { map  } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { tap, scan, take} from 'rxjs/operators';


// Options to reproduce firestore queries consistently
interface QueryConfig {
  path: string, // path to collection
  field: string, // field to orderBy
  limit?: number, // limit per query
  reverse?: boolean, // reverse order?
  prepend?: boolean // prepend to source?
}

@Injectable()
export class PaginationService {

  // Source data
  private _done = new BehaviorSubject(false);
  private _loading = new BehaviorSubject(false);
  private _data = new BehaviorSubject([]);

  private query: QueryConfig;

  // Observable data
  data: Observable<any>;
  done: Observable<boolean> = this._done.asObservable();
  loading: Observable<boolean> = this._loading.asObservable();

  constructor( private afs: AngularFirestore ) { }

  // Initial query sets options and defines the Observable

  init(path, field, opts?) {

    this.query = { 
      path,
      field,
      limit: 10,
      reverse: false,
      prepend: false,
      ...opts
    }

    const first = this.afs.collection(this.query.path, ref => {
      return ref
             .orderBy(this.query.field, this.query.reverse ? 'desc' : 'asc')
              .where('Active', '==', 'Y')
               .limit(this.query.limit)             
    })

    this.mapAndUpdate(first)

    // Create the observable array for consumption in components
    this.data = this._data.asObservable()
        .pipe(scan( (acc, val) => { 
          return this.query.prepend ? val.concat(acc) : acc.concat(val)
        }))

  }

  // more() Retrieves additional data from firestore
// This works the first time but not the second time 

  more() {
    const cursor = this.getCursor()
    const more = this.afs.collection(this.query.path, ref => {
      return ref
              .orderBy(this.query.field, this.query.reverse ? 'desc' : 'asc')
              .limit(this.query.limit)
              .startAfter(cursor)
    })
     this.mapAndUpdate(more)
  }

  // Determines the doc snapshot to paginate query 
  private getCursor() {
    const current = this._data.value
    if (current.length) {
      return this.query.prepend ? current[0].doc : current[current.length - 1].doc 
    }
    return null
  }


  // Maps the snapshot to usable format the updates source
  private mapAndUpdate(col: AngularFirestoreCollection<any>) {

    if (this._done.value || this._loading.value) { return };

    // loading
    this._loading.next(true)

    // Map snapshot with doc ref (needed for cursor)
    return col.snapshotChanges()
      .pipe(tap(arr => {
        let values = arr.map(snap => {
          const data = snap.payload.doc.data()
          const doc = snap.payload.doc
          return { ...data, doc }
        })

        // If prepending, reverse array
        values = this.query.prepend ? values.reverse() : values

        // update source with new values, done loading
        this._data.next(values)
        this._loading.next(false)

        // no more values, mark done
        if (!values.length) {
           this._done.next(true)
        }
    }))
    .pipe(take(1))
    .subscribe()

  }

  // Reset the page
  reset() {
    this._data.next([])
    this._done.next(false)
  }
 }

1 个答案:

答案 0 :(得分:0)

当用户达到定义的距离时,将调用分配给ionInfinite事件的表达式。此表达式完成所有任务后,应在无限滚动实例上调用complete()方法。

就您而言,您尚未处理事件,只是通过了...

this.page.more($event)

如下更改模板html:

<ion-infinite-scroll threshold="100px" (ionInfinite)="findNext($event)">

在Component.ts中添加方法:

async findNext($event) {
    setTimeout(async () => {
      await this.page.more();
      $event.target.complete();
    }, 500);
  }