Angular Declarative Approach
AngularRxJS
A declarative programming paradigm concerned with data streams and the propagation of change. Code is reactive
when an input change leads to an automatic change in output.
Reactive Development
- React to user actions
- React to state changes
- Combine data streams
- Be resilient to failure
- Manage state
- Communicate between components
Going Reactive:
- Working with Observables
- Leverage RxJS operators directly
- Improve performance
- Simplify code, making it easier to read and understand
Think RxJS like a conveyor.
- Conveyor Start (Emits items) —
subscribe - Item passes through a set of operations —
pipe - As an observer: Next item, process it —
next(); Error occurred, handle it —error(); Complete, you're done —complete() - Stop —
unsubscribe
ChangeDetectionStrategy.OnPush
Improves performance by minimizing change detection cycles.
Component is only checked when:
@Inputproperties change- Event emits
- A bound Observable emits
Procedural Example
export class HomeComponent {
// This feels declarative, but we have to look at all the places that itemsLeftInStock is used to understand how the logic works.
itemsLeftInStock = this.itemService.getItemsLeftInStock();
buyItem() {
this.itemsLeftInStock = this.itemsLeftInStock - 1;
}
}
Declarative Approach
- Leverage the RxJS Observable and operators' power
- Combine data streams
- React to user actions
Declarative vs Procedural approach
Procedural Approach: Pass Parameters
// product.service.ts
getProducts(page: number) {
return this.http.get<Product[]>('/api/products', {params: {page}});
}
// component
this.productService
.getProducts(1)
.subscribe({next: (products) => (this.products = products), error: (err) => (this.errorMessage = err)});
Declarative Approach: React to Actions ❤️
// product.service.ts
products$ = this.requestedPage$.pipe(switchMap((page) => this.http.get<Product[]>('/api/products', {params: {page}})));
// component
products$ = this.productService.products$.pipe(
catchError((err) => {
this.errorMessage = err;
return EMPTY;
}),
);
operators
Combination operator
- combine to single:
merge,concat - flatten higher-order Observable:
mergeAll - emit combined value:
combineLatest,forkJoin,withLatestFrom
Caveats
-
Avoid relying too heavily on
this.variablein Angular component functions, because it's hard to trace where the variable gets changed. You need lots of context to understand what's going on. Unit testing is also harder.Subscribing to variables and assigning variables inside the subscribe next function follows this approach.