Skip to content

[Angular | RxJS] BehaviorSubject with custom states

Georg Höller
Georg Höller
2 min read
[Angular | RxJS] BehaviorSubject with custom states
Photo by Clark Tibbs / Unsplash

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.

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 :)
Angular

Comments


Related Posts

Members Public

[Angular] Dynamic App Config and Translations in a Submodule

When I'm setting up a new angular application, I always start with translation support and an external app config json loader. You should never start a new app without an internationalization (i18n) system - even if you only support one language! We are going to use the package @ngx-translate/core.

[Angular] Dynamic App Config and Translations in a Submodule
Members Public

[Capacitorjs | Angular] Back Swipe Support for iOS and Android

Recently I recognized that capacitorjs apps does not have a native back swipe support. Little bit weird I thought but the solution is quite simple! iOS - Native To enable the iOS back swipe we need to write a capacitor plugin. I tried it without a plugin but inside the

[Capacitorjs | Angular] Back Swipe Support for iOS and Android
Members Public

[Angular] Routing Helper - a Typesafe Routing Attempt

It always bothered me that you have to define the routing paths as strings. If a route changes, you have to change all references manually. There must be a better way! Routing Constants The constants are for your specific app - here you can define a class which represents your

[Angular] Routing Helper - a Typesafe Routing Attempt