We are querying graphql
over websocket to have a subscription to a list of entities. We use pagination as the list is supposed to be scrollable indefinitely. We also want to see changes made in db in real time in the list on the front-end.
This list can be filtered
, sorted
and paginated
.
We have the class SelectParams
that helps us to do that:
export class SelectParams {
page = 0;
query = '';
sort: Sort = { sortBy: 'creationDate', sortOrder: 'DESC' };
take = 30;
}
Then when we want to subscribe to a list we just give a selectParams$ = Observable<SelectParams>
to get the list that is "reactive" and reacts to the changes in selectParams$
. This is used like this:
this.items$ = this.featureSrv.selectMany(this.selectParams$)
Under the hood the selectMany
works like this:
// 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), [])
);
The method this.apolloWrapper.selectMany
is a method we created that just does a subscription over websocket to some slice of the data.
Say we had page = 0
then this.apolloWrapper.selectMany
will return a subscription to the first slice of data, then when page = 1
, this.apolloWrapper.selectMany
returns a subscription to the second slice of data.
The problem arise when we load the second page and make a modification to items to the first slice, the scan
is then triggered and items are readded to the end of the list.