This article is part of the Ionic Framework / Capacitor / Stencil Advent Calendar 2021.
When a request fails, showing a Toast with code like the following is often enough:
async getData(): Observable<Type> {
return this.httpClient.get<Type>(url)
.pipe(
catchError((e) => {
this.toastController.create({ message: /.../, duration: 3000 }).then(d => d.present());
return throwError(() => e);
})
)
}
In real projects, though, you sometimes want to show an alert instead of a quick Toast. Something like this:
async getData(): Observable<Type> {
return this.httpClient.get<Type>(url)
.pipe(
catchError((e) => {
this.alertController.create({
header: 'Error',
message: 'The error details go here',
buttons: [
{
text: 'Close',
},
],
}).then(d => d.present());
return throwError(() => e);
})
)
}
However, this has one big problem: the alert is handled asynchronously. Specifically, the error is thrown before the user taps Close on the alert, so processing finishes too early. For example, if you write the page component like this, you have a problem:
getData().subscribe({
next: () => { /../ },
error: () => {
// Close the modal because the operation failed
this.modalController.dismiss();
},
})
When this runs, the error is thrown before the alert is dismissed, so the modal closes. When you want to “handle the error synchronously,” write it like this instead of the code above:
async getData(): Observable<Type> {
return this.httpClient.get<Type>(url)
.pipe(
catchError(error => from(new Promise(async (resolve, reject) => {
const alert = await this.alertController.create({
header: 'Error',
message: 'The error details go here',
buttons: [
{
text: 'Close',
handler: () => reject(error),
},
],
});
return await alert.present();
}).then(() => undefined))),
)
}
To receive catchError as an Observable, convert the Promise with from(). You can write freely inside the Promise, but keep two things in mind. First, because this is error handling, make sure to reject. In practice, I reject inside the handler. Second, you need to chain .then() on the Promise itself. Without that, the type becomes unknown and you cannot handle types correctly.
I received advice on this from laco of Japan Angular User Group. Thank you very much!!
See you next time.