AngularInDepth is moving away from Medium. More recent articles are hosted on the new platform inDepth.dev. Thanks for being part of indepth movement!

As a good developer, you always notify the end-user about the current status of an application by showing either loading indicator or error message.

Async Pipe

Using async pipe is quite common scenario in many Angular applications.

It is as simple as that:

class AppComponent {

obs$ = of(1).pipe(delay(500));

} <div>

{{ obs$ | async }}

</div>

Async pipe takes care of subscribing to Observable. It also marks component for check and you don’t worry about unsubscribing.

Built-in *ngIfElse solution

Let’s try to show loading indicator while an underlying async operation is being resolved:

<div *ngIf="obs$ | async as obs; else loading">

{{ obs }}

</div> <ng-template #loading>Loading...</ng-template>

We leverage as keyword to assign the result of resolved observable to the obs local variable. After that, we use the ngIfElse conditional property to display loading indicator if obs get a falsy value.

At first glance, it looks like a great solution in most cases but let’s discover…

The problems

Let’s change our observable a bit so that it returns a falsy value:

obs$ = of(0).pipe(delay(500))

We’re stuck at this screen:

2. Let’s simulate an error in our stream:

obs$ = of(1).pipe(

delay(500),

map((x: any) => x()),

);

We see the same screen again:

3. Let’s imagine we use some component that takes loading property as an Input. How would you pass that property?

<div *ngIf="obs$ | async as obs; else loading">

{{ obs }}

</div> <ng-template #loading>Loading...</ng-template> <ng-select [loading]="?????"

All of these problems above introduce a complementary need to some additional code that can be then duplicated again and again across the application.

Custom WithLoadingPipe to the rescue

Not too long ago I posted a solution on twitter where I suggested creating a custom pipe to handle loading behavior.

Here’s a simple example of how we can leverage that pipe:

<div *ngIf="obs$ | withLoading | async as obs">

<ng-template [ngIf]="obs.value">Value: {{ obs.value }}</ng-template>

<ng-template [ngIf]="obs.error">Error {{ obs.error }}</ng-template>

<ng-template [ngIf]="obs.loading">Loading...</ng-template>

</div>

Let’s try all the cases we’ve mentioned above with this pipe: