How to combine a pagination with Observables and the AsyncPipe in Angular 9?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Pagination fits naturally with Angular observables when the current page is treated as a stream of state instead of as a mutable field with manual subscriptions. The AsyncPipe then renders the latest page result and handles subscription cleanup automatically.
The practical pattern is to represent the page number with an observable, turn page changes into HTTP requests with switchMap, and expose a single observable view model to the template.
Model Page Changes Reactively
Start with a service that fetches one page from the backend:
In the component, model the current page as a BehaviorSubject:
switchMap is the important operator here. If the user clicks pages quickly, older requests are abandoned and only the latest page result is allowed to update the UI.
Bind the Result with AsyncPipe
The template can stay declarative:
The AsyncPipe subscribes to vm$, pushes the latest response into the template, and unsubscribes when the component is destroyed. That removes most of the manual cleanup code that older Angular examples often rely on.
Combine Pagination with Other State
Real screens usually have more than page number. Search terms, sort order, filters, and refresh triggers can all be modeled as streams and combined with pagination:
This keeps the component honest about what drives the request. Instead of manually calling loadPage() from several places, the request becomes a function of reactive state.
Why This Pattern Is Preferable
The imperative version usually means a mutable page field, a loadPage() method, explicit subscriptions, and side effects that assign values into component fields. That works for a toy example, but it becomes hard to maintain once quick navigation, filtering, and cancellation matter.
Using observables plus AsyncPipe keeps the flow simple: state streams in, HTTP streams out, and the template renders the latest view model. That is easier to test and easier to extend.
Common Pitfalls
- Using
mergeMapinstead ofswitchMap, which can let stale responses overwrite newer page results. - Subscribing manually in the component and also using
AsyncPipe, creating duplicated data flow. - Omitting metadata such as
totalandpageSize, which makes disabling navigation controls awkward. - Treating pagination as imperative mutable state instead of as part of the observable pipeline.
Summary
- Represent the current page as an observable, commonly with
BehaviorSubject. - Use
switchMapso only the latest page request updates the UI. - Expose one observable view model and bind it with
AsyncPipe. - Compose pagination with search or sorting by combining streams rather than calling load methods manually.
- Prefer the observable plus
AsyncPipepattern over subscription-heavy imperative pagination code.

