2

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.

Ced
  • 15,847
  • 14
  • 87
  • 146

0 Answers0