In this post, I am going to show you a very simple yet effective way of handling errors when using Async Pipe in Angular. If you are new to async pipe, it basically allows you to subscribe to observables and promises right inside your templates. Below is a simple example of how you can use Async Pipe in Angular.
<ng-container *ngIf="some_observable | async"></ng-container>
Async Pipe has two major advantages. First, since subscriptions are handled automatically, you don’t need to worry about unsubscribing to observables. That will also be handled automatically for you. And secondly, it works with OnPush change detection automatically out of the box. You can learn more about change detection in Angular here and about Async Pipe here.
In order to handle errors properly, we are going to use *ngIf
with async pipe. Even when using *ngFor
, we need to wrap it around an *ngIf
container. This is because *ngIf
provides else
statement, allowing us to provide a template if the operation fails or the request is still in progress. This not only allows us to display an error message but also to show a loading indicator. Once the async operation has been resolved successfully, then the data object can be passed to the *ngFor
loop for looping if it’s a list. Without further ado, let’s get started.
Getting Started
As always, we will start by creating a new angular project, using Angular CLI.
$ ng new ng-async-pipe-error-handling
Next, we need to install bootstrap for our User Interface, feel free to use your favorite package manager.
$ yarn add bootstrap
$ npm install bootstrap
And then, import Bootstrap CSS into your angular project, under your app style section in angular.json.
"styles": [
"src/styles.scss",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
That’s it for our project setup.
Catching Async Errors
First, we need to be able to determine when our async requests returns an error. To do this, in our component, we are going to have an error property, and set it to null.
public errorObject = null;
The type of the error object may vary depending on the type of request. For instance, if you are running a HTTP request, you might want to use HttpErrorResponse instead of any. Then, we will use the catchError RXJS operator, to tap into the observable. Then, we will set the errorObject
to error we catch inside catchError
operator.
this.errorObject = null;
this.asyncOps$ = asyncService.listOfCountries().pipe(
catchError(err => {
this.errorObject = err;
return throwError(err);
})
);
In simple terms, we are tapping into the response of the async request, then checking if there is any error. If an error is caught, we are setting a property of the component class with the error object. Then, inside our template, we will check whether this property is set. If set, we shall parse and display error, otherwise we shall just show a loading animation.
Displaying a Loading Animation or Error Message
Next, inside our template we are going to use async pipe to subscribe to the observable. But, we will use else statement, to show a different template, when the async request is in progress or fails.
<ng-container *ngIf="asyncOps$ | async as data; else loadingOrError">
<!-- code here -->
</ng-container>
The loadingOrError(ID)
refers to a template which shall have a template for a loading animation or an error message.
<ng-template #loadingOrError> <!-- some code here --> </ng-template>
Inside the loadingOrError template, two possible messages can be displayed. The first one is a loading animation, indicating async request is in progress. And the second option shall display an error, in cases where async request is completed but with error. First, we shall check whether the error object is set. If it’s not set show a loading message or animation, otherwise show the error message.
<ng-container *ngIf="errorObject; else loading">
<div class="col-12 text-center">
<div class="alert alert-danger">
{{ errorObject }}
</div>
</div>
</ng-container>
<ng-template #loading>
<div class="col-12 text-center">
Loading ...
</div>
</ng-template>
Source Code
You can find the source code of this post here.