[Angular | RxJS] BehaviorSubject with custom states
Whenever I call an api, often my frontend has to go through some different visualization steps. Imagine you have some kind of search input and a list which displays the results but you want to show an loading indicator, an error hint and something different when the result is empty.
![](https://georghoeller.dev/content/images/2023/04/image.png)
State BehaviorSubject
This is where the StateBehaviorSubject comes in handy. First, let's have a look at the source code:
import { BehaviorSubject, Observable } from 'rxjs';
export enum BehaviorStates {
Empty,
Loading,
Ready,
Error,
}
export class StateBehaviorSubject<T> extends BehaviorSubject<T> {
private state: BehaviorSubject<BehaviorStates> =
new BehaviorSubject<BehaviorStates>(BehaviorStates.Empty);
asStateObservable(): StateObservable<T> {
const observable: any = new StateObservable<T>(this.state.asObservable());
observable.source = this;
return observable;
}
empty() {
this.state.next(BehaviorStates.Empty);
}
loading() {
this.state.next(BehaviorStates.Loading);
}
override next(value: T): void {
super.next(value);
this.state.next(BehaviorStates.Ready);
}
override error(err: any): void {
this.state.next(BehaviorStates.Error);
}
}
export class StateObservable<T> extends Observable<T> {
public state$!: Observable<BehaviorStates>;
constructor(state: Observable<BehaviorStates>) {
super();
this.state$ = state;
}
}
The StateBehaviorSubject extends from BehaviorSubject but it adds a variable state which is a BehaviorSubject itself. To set the current state I added a method for each possible state.
To be able to work with an Observable which also contains the state, I created the method asStateObservable - this should be self-explaining.
Example usage
I use my order service for example. I normally define a private StateBehaviorSubject and a public StateObservable inside a service.
export class OrderService {
private _orders: StateBehaviorSubject<IOrderModel[]> =
new StateBehaviorSubject<IOrderModel[]>([]);
orders$: StateObservable<IOrderModel[]> = this._orders.asStateObservable();
constructor(private dialog: Dialog) {
this.loadOrders();
}
loadOrders() {
this._orders.loading();
setTimeout(() => {
this._orders.next([
{
id: '123',
orderType: OrderType.Delivery,
title: 'Bowl',
restaurantUrl: 'https://www.someurl.at/',
orderDeadline: new Date(2023, 3, 6, 10, 30),
paymentUrl: 'https://www.paypal.com/paypalme',
description: 'bruh, it is monday again.',
},
]);
}, 5000); // simulate loading
}
}
Initial the subject's state is empty
by default, when loadOrders
getting called it changes to loading
and after 5 seconds the next
call changes it to ready
.
Now let's display the orders in a smart component which is importing the orderService
via DI.
export class SomeComponent {
constructor(public orderService: OrderService) {}
}
<div *ngIf="orderService.orders$ | async; let orders" class="h-full w-full">
<order-list
[orders]="orders"
[state]="(orderService.orders$.state$ | async)!"
></order-list>
</div>
The dump component order-list
takes the state from the StateObservable and is always displaying the right template.
Teaser
The angular async pipe is a nice tool but sometimes we need more. In the next blog post I will show you a better way using our newly created StateBehaviorSubject. Consider subscribing to my newsletter! :-)
If you have further questions about this topic or need help with a problem in general, please write a comment or simply contact me at yesreply@georghoeller.dev :)
Georg Hoeller Newsletter
Join the newsletter to receive the latest updates in your inbox.