Skip to content

Instantly share code, notes, and snippets.

@dmitry-stepanenko
Last active September 30, 2020 05:15
Show Gist options
  • Save dmitry-stepanenko/161dc0c52a6a13067ec7011dec9525f7 to your computer and use it in GitHub Desktop.
Save dmitry-stepanenko/161dc0c52a6a13067ec7011dec9525f7 to your computer and use it in GitHub Desktop.
import { Observable, EMPTY } from 'rxjs';
import { expand, reduce } from 'rxjs/operators';
export type Request<T> = (pageNumber: number, response?: T) => Observable<T>;
export type HasMore<T> = (response: T, pageNumber: number) => boolean;
export type JoinResults<T> = (accumulator: T, response: T) => T;
export interface Config<T> {
/**
* function to make a request. Should accept a page number and return an observable to make a request.
* Accepts a `response` as a second argument. Be careful, `response` will be undefined for the first request
*/
request: Request<T>;
/**
* function to determine if there're more results. Accepts a response of previous request
*/
hasMore: HasMore<T>;
/** function to join responses */
joinResults: JoinResults<T>;
}
/**
* Method to recursively call api to load all data. Emits once all results are fetched
*/
export function initiateRecursiveCall<T>({ request, hasMore, joinResults }: Config<T>): Observable<T> {
return request(1).pipe(
expand((response, index) => {
// using index + 1, because index starts from 0
const currentPageNum = index + 1;
if (!hasMore(response, currentPageNum)) {
return EMPTY;
}
return request(currentPageNum + 1, response);
}),
reduce((acc, val) => joinResults(acc, val))
);
}
// USAGE
import { HttpClient, HttpParams } from '@angular/common/http';
interface ItemsResponse {totalCount: number, items: T[] }
export class HttpService {
constructor(private http: HttpClient) {}
/** method to be used to load all items */
getItems(): Observable<ItemsResponse> {
const perPage = 100;
return initiateRecursiveCall({
request: (pageNumber) => this.getItemsRequest(pageNumber, perPage),
hasMore: (staffResponse, pageNumber) => staffResponse.totalCount >= perPage * pageNumber,
joinResults: (acc, response) => ({ totalCount: response.totalCount, items: acc.items.concat(response.items) }),
});
}
/** actual http request with offset and count. */
private getItemsRequest<T>(pageNumber: number, perPage: number): Observable<{ItemsResponse> {
// setting params to the get request
const params = new HttpParams({
fromObject: {
count: perPage,
offset: (pageNumber - 1) * perPage,
},
});
return this.http.get(url, { params })
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment