ウェブサイト検索

TypeScript - RxJSで各ページへのサブスクリプションを伴うページネーション


私たちはWebSocketを介してgraphqlをクエリして、エンティティのリストへのサブスクリプションを行っています。リストは無限にスクロール可能であることが想定されているため、ページネーションを使用しています。また、フロントエンドのリストでデータベースに行われた変更をリアルタイムで確認したいと考えています。

このリストはフィルタリング、ソート、ページ分割できます。

私たちはそれを行うのに役立つ SelectParams クラスを持っています。

export class SelectParams {
    page = 0;
    query = '';
    sort: Sort = { sortBy: 'creationDate', sortOrder: 'DESC' };
    take = 30;
}

それからリストを購読したいときは、単に selectParams$=Observable<SelectParams> を渡して、selectParams$ の変更に反応する「リアクティブ」なリストを取得します。これは次のように使われます:

    this.items$ = this.featureSrv.selectMany(this.selectParams$)

内部では、selectMany は次のように動作します:

// when we call selectMany we just push the params to the pipeline
selectMany(params$: Observable<SelectParams> = of(new SelectParams())): Observable<T[]> {
    this.selectManyParams$.next(params$);
    return this.selectMany$;
}

// subject where we push params to sort, paginate and filter
selectManyParams$ = new ReplaySubject<Observable<SelectParams>>(1);
// when the params change then so does this observable, which is returned by the selectMany function
selectMany$ = this.selectManyParams$.asObservable().pipe(
    // retrieve params from their observable form
    flatMap(params$ => params$),
    // when the params haven't changed we shouldn't do anything
    distinctUntilChanged(),
    // then we query graphql to get a suscription to some part of the data
    switchMap(({ page, sort, query, take }: SelectParams) => {
    // the selectMany here is a subscription to some data on the server
        return this.apolloWrapper.selectMany(this.gql, page, sort, query, take ).pipe(
    // we add page data so we can use it in the scan
            map(data => ({ data, page }) as any)
        );
    }),
    // we append the result if page was incremented
    // else we just return the result
    scan((acc: any, curr: any) => curr.page === 0 ? curr.data : acc.concat(curr.data), [])
);

メソッド this.apolloWrapper.selectMany は、私たちが作成したメソッドで、データの一部に対して WebSocket を通じたサブスクリプションを行うだけのものです。例えば、page=0 の場合、this.apolloWrapper.selectMany はデータの最初のスライスに対するサブスクリプションを返します。そして page=1 の場合、this.apolloWrapper.selectMany はデータの二番目のスライスに対するサブスクリプションを返します。

問題は、2ページ目を読み込み、最初のスライスのアイテムを変更したときに発生します。その後、scan がトリガーされ、アイテムがリストの末尾に再追加されます。