rxjs нумерация страниц с подпиской на каждую страницу - PullRequest
0 голосов
/ 04 июля 2018

Мы запрашиваем graphql через websocket, чтобы подписаться на список объектов. Мы используем разбиение на страницы, так как список должен прокручиваться бесконечно. Мы также хотим видеть изменения, сделанные в БД в режиме реального времени, в списке на внешнем интерфейсе.

Этот список может быть filtered, sorted и paginated.

У нас есть класс 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 - это созданный нами метод, который просто выполняет подписку через веб-сокет на некоторый фрагмент данных. Допустим, у нас было page = 0, тогда this.apolloWrapper.selectMany вернет подписку на первый фрагмент данных, а затем, когда page = 1, this.apolloWrapper.selectMany вернет подписку на второй фрагмент данных.

Проблема возникает, когда мы загружаем вторую страницу и вносим изменения в элементы первого среза, затем запускается scan и элементы считываются в конец списка.

...