I have an observable that is pulling 12 people from a directory and displaying onto a template with async pipe. The API it hits is fairly basic and just uses offset pagination direct pass into the SQL query to grab the people. What I am hoping for is we have on first load, the API returns the first 12 people, then we have a 'load more' button. Hoping this button can keep the 12 people already on the screen but load in 12 more by changing the offset so first call is offset 0, next is offset 11 etc
I was looking at things like forkJoin or shareReplay because I want this to be reactive and also want to avoid asking the API for larger and larger response objects - example on load more would not want to ask for 24 just the next 12
so right now onInit I have the first of the people being pulled in
public people$: Observable<Directory[]> = combineLatest([this.selectedLocation$,this.selectedDepartment$, this.selectedSearchQuery$]).pipe(
switchMap(([loc,dept,query]) =>
this.apiEmployeeService.getDirectory({
dept: dept?.toString(),
location: loc === 'Both' ? '%' : loc,
offset: this.offset,
limit: 12,
search: query
}),
)
so what can I have the load more button call from the HTML to keep existing 12 people but then refire this observable but with an offset of 12
CodePudding user response:
We can use a BehaviorSubject
as a trigger to fetch more and use the scan
operator accumulate the results from multiple fetches into a single observable:
const ITEMS_PER_PAGE = 12;
export class AppComponent {
private fetch$ = new BehaviorSubject<void>(undefined);
public people$: Observable<Employee[]> = combineLatest([
this.selectedLocation$,
this.selectedDepartment$,
this.selectedSearchQuery$,
]).pipe(
switchMap(([loc, dept, search]) => this.fetch$.pipe(
switchMap((_, pageIndex) => this.apiEmployeeService.getDirectory({
dept : dept?.toString(),
location : loc === 'Both' ? '%' : loc,
offset : pageIndex * ITEMS_PER_PAGE,
limit : ITEMS_PER_PAGE,
search
})),
scan((all, page) => all.concat(page), [])
)),
);
fetchMore() {
this.fetch$.next();
}
}
The reason for the nested switchMap
is because I'm assuming that if any of the query criteria changes (location, dept, search), then you want the offset
to reset back to 0.
The second parameter of the switchMap callback receives the "emission index". We can use that to represent the "page number". Whenever the combineLatest
emits a new value, a fresh subscription to fetch$
occurs, effectively resetting the index.
Here's a little StackBlitz where you can see this behavior in action.