Мы запрашиваем 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
и элементы считываются в конец списка.