×

iFour Logo

Error Handling using Angular RxJS

Kapil Panchal - February 05, 2021

Listening is fun too.

Straighten your back and cherish with coffee - PLAY !

  • play
  • pause
  • pause
Error Handling using Angular RxJS

RxJS is the biggest part of Angular. With a well understanding of how to implement error handling the right way with RxJS, you are sure to run with strange problems down the line when an error does occur. By contrast, if you know what you are doing up a cover, you can remove those strange problems and save yourself some debugging distress.

 

Table of Content

In this blog, we will discuss the type of RxJS observables to be most involved, how to incorrectly handle an error through RxJS, what happens when you do not handle an error applying RxJS and how to precisely handle errors applying RxJS.

Infinite Observables


This blog will be linked with infinite observables, those that you hope to keep getting values from. That is because if you do error handling incorrectly, they stop to be infinite observables and finish, which will be very bad. After all, your app is expecting it to be infinite.

These will be considered:

  • DOM Event: A DOM Event ‘keyup’ that you wanna debounce as the user types on the page and then look up through an API.

  • NgRx Effect: An NgRx Effect that you hope will forever be listening for dispatched actions

DOM Event Case Study


That first consideration will concentrate on handling DOM events and doing searches based on them. There will be two input boxes that you type in a character's name of Star Wars. While you stop typing for 300 milliseconds and the text is changed. It will search for those names through the Star Wars API and display results. The first input box will keep working after an error. The second input box will halt working after an error.

Here is the interface:

ai-Hiring-banner

Figure: DOM Event case study interface

We have gamed this a pretty bit so that if you type “error”, it will search over a wrong URL, therefore creating an error.

Here is the appropriate HTML:



                                        

The key up event normally emits through the “next” method of the Subject.

Following is the component code:

 
  import { Component } from '@angular/core';
  import { Subject } from 'rxjs';
  import { finalize } from 'rxjs/operators';
  import { RxJSDemoService } from './rx-jsdemo.service';
  
  @Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
  })
  export class AppComponent {
    title = 'RxJSErrorHandlingDemo';
    searchTerm$ = new Subject();
    searchTermError$ = new Subject();
    resultsError: any;
    results: any;
    constructor(
      private rxjsDemoService: RxJSDemoService,  
    ) {}
  
    ngOnInit(): void {
      this.rxjsDemoService
        .searchBadCatch(this.searchTermError$)
        .pipe(
          finalize(() =>
            console.log("searchTermError$ (bad catch) finalize called!")
          )
        )
        .subscribe(results => {
          console.log("Got results from search (bad catch)");
          this.resultsError = results.results;
        });
  
      this.rxjsDemoService
        .search(this.searchTerm$)
        .pipe(finalize(() => console.log("searchTerm$ finalize called!")))
        .subscribe(results => {
          console.log("Got results from search (good catch)");
          this.results = results.results;
        });
    }
  }
  
                                        

This code essentially will be reporting results to the page and logging either it was called or not. Note that we are calling two various service methods and passing in two several subjects.

The error handling code for this case study is in the following rxjsDemoService :

 
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { catchError } from 'rxjs/operators'; 
import { Observable } from 'rxjs';
import { HttpClient } from "@angular/common/http";

@Injectable({
  providedIn: 'root'
})
export class RxJSDemoService {
  constructor(private http: HttpClient){}    
  
  searchBadCatch(terms: Observable) {
    return terms.pipe(
      debounceTime(300),
      distinctUntilChanged(),
      switchMap(term => this.searchStarWarsNames(term)),
      catchError(error => {
        console.log("Caught search error the wrong way!");
        return of({ results: null });
      })
    );
  }

  search(terms: Observable) {
    return terms.pipe(
      debounceTime(300),
      distinctUntilChanged(),
      switchMap(term =>
        this.searchStarWarsNames(term).pipe(
          catchError(error => {
            console.log("Caught search error the right way!");
            return of({ results: null });
          })
        )
      )
    );
  }

  private searchStarWarsNames(term) {
    let url = `https://swapi.co/api/people/?search=${term}`;
    if (term === "error") {
      url = `https://swapi.co/apix/people/?search=${term}`;
}

    return this.http.get(url);
  }  
}

                                        

Bad Error Handling


The searchBadCatch method has our bad error handling code. Just looking at it, it looks fine. It's debouncing for 300 ms, has the distinctUntilChanged to ensure we do not search for the same thing twice in a row. There is a switchMap that calls the searchStarWarsName method and we are catching errors through the catchError method.

If you catch the error through catchError at the first step of the Observables pipe method. It will enable you to handle the error and return one more result in the stream but will then finish the observable stream. And that indicates it won't listen to key up events anymore. Thus, at all costs never allow an error to percolate to this step.

Remark that if catchError is given on the initial level of the Observable pipe method, the finalize operator will be called. You can see this up in the component code.

Following is the code of it:

ai-Hiring-banner

Figure: Component code

Never let an error permeate to the level of the red line.

Good Error Handling


The search method has our RxJS finest practice error handling code:

Always put the catchError operator within a switchMap so that it only ends the API call stream and then returns the stream to the switchMap, which remains the Observable.

If you are not calling an API, ensure to add a try or catch block so that you can handle the error in the catch block and not allow it to permeate to the first level pipe. Do not believe your code will never fail, use a try or catch block.

So, you can see in the code that we add a pipe to the searchStarWarsNames call so that within of there, we catchError and therefore not allow the error to permeate to the first level pipe.

And the best practice code is the following:

ai-Hiring-banner

Figure: Best practice code

Always catch errors within the switchMap/mergeMap/concatMap, etc.

Output


Now it is time to see how this works on the website. We can imagine that it works at first. The fun starts when an error is generated from the API call.

First,we will type error in both input boxes as shown:

ai-Hiring-banner

Figure: Output

We will quite it as an exercise for you to see the console output. Now for the actual test cantype in something and get results later handling the error?

Here we see that the first one works and the second one does not work anymore:

ai-Hiring-banner

Figure: console output

The second input box is what we were talking about over a Weird problem in the intro. You would have a difficult time estimating out why your search quit working.

Looking to Hire Angular Developer ? Your Search ends here.

NgRX Effect Case Study


The reason we began writing this blog was that we had a weird problem with one of our apps that was through NgRX and Effects. See for information on effects.

The interface is the following:

ai-Hiring-banner

Figure: NgRX and Effects Interface

Nothing desires here:

  1. The success button calls the Star Wars API with "person/1" (Luke Skywalker) and gives the name in the output on the screen.

  2. Error-stops listening button calls the API with an incorrect URL so it generates an error message "catch is done wrong so it stops listening for the effect".

  3. Error-Don't catch error button calls the API with an incorrect URL so it generates an error message "not catching the error".

  4. Error-Keeps Listening button calls the API with an incorrect URL so it generates an error message "properly catching the error so you can click it multiple times".

We will skip the HTML for this because it is just buttons calling component methods. Following is the component code:

 
import { Component, OnInit } from "@angular/core";
import { catchError } from "rxjs/operators";
import { of, throwError, Observable } from "rxjs";
import { Store, select } from "@ngrx/store";

import { RxjsService } from "./services/rxjs.service";
import * as fromRoot from "./store/reducers";
import {
  CallWithoutError,
  CallWithError,
  CallWithErrorKeepListening,
  CallWithErrorNotCaught,
  EffectReturnTest
} from "./store/app.actions";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  title = "RxJS Playground";
  name$: Observable;
  someString$: Observable;

  constructor(
    private rxjsService: RxjsService,
    private store: Store
  ) {}

  ngOnInit(): void {
    this.rxjsService.subject
      .pipe(
        catchError(error => {
          console.log("Error in catchError", error);
          return of(error);
        })
      )
      .subscribe(
        value => {
          console.log("Subject value:", value);
        },
        error => console.log("Error!", error)
      );

    this.name$ = this.store.pipe(select(fromRoot.getName));
    this.someString$ = this.store.pipe(select(fromRoot.getSomeString));
  }

  pokeSubject(value: boolean) {
    this.rxjsService.nextSubject(value);
  }

  errorSubject() {
    this.rxjsService.errorSubject();
  }

  ngrxSuccess() {
    this.store.dispatch(new CallWithoutError());
  }

  ngrxError() {
    this.store.dispatch(new CallWithError());
  }

  ngrxErrorKeepListening() {
    this.store.dispatch(new CallWithErrorKeepListening());
  }
  ngrxErrorDontCatch() {
    this.store.dispatch(new CallWithErrorNotCaught());
  }

  ngrxEffectReturnTest(actionNum) {
    this.store.dispatch(new EffectReturnTest(actionNum));
  }
}

                                        

The error handling excellent, evil, and ugly is in the effected code.

CallWithoutError Effect


Following is our success case:

This individual will work each time but if it failed, it would remain working because the catchError is inside the http.get pipe. For the resolution case, the SetName reducer will add the “name” to the store. The UI plucks that up and displays it.

CallWithError Effect


This effect will call the API with the wrong URL so an error is caused. The error handling is done wrongly so once called, this will never work repeatedly unless the app is refreshed.

In this case, the catchError at the first level of this action$.pipe will get called, thus ending the effect since its Observable stream will end. This is much like in the case study above through many RxJS Observables. We should see the message "Error-You're Fasted" on the page after clicking it. If we try clicking the button return, it will not fire the effect.

Following is the output for this:

ai-Hiring-banner

Figure: Output of callWithoutError Effect

CallWithErrorKeepListening Effect


This effect will call the API through the incorrect URL so an error is generated. Despite that, it will handle the error properly so that it can be called again.

The correct way to handle the RxJS error is by putting the catchError within the http.get pipe. It will end the http.get observable but that does not matter because it's a finite observable anyways and only emits one value. While it returns the SetName action, the switchMap will release it and remain the Observable stream. Notice that the finalize there will never be called.

Following is the output of this:

ai-Hiring-banner

Figure: Output of callWithErrorListening Effect

CallWithErrorNotCaughtEffect


This our last effect that describes what happens if we do not catch the error? It behaves the same way as if we handled the error incorrectly. It is just that you are not looking into that error stream.

Also, the name on the UI will not be set after you aren't calling setName in the catchError operator. So, you will moreover not see any output if it was the first button clicked or you will see the last name that was set. Another mysterious problem that would be difficult to debug.

Conclusion


As you can tell from this blog, knowing how to appropriately handle RxJS errors in your Angular app will help you to prevent those weird problems you could see when your infinite Observable stream ends unexpectedly. Through this knowledge, you should be able to make sure that your infinite observables never and unless you decide they are finished.

Error Handling using Angular RxJS RxJS is the biggest part of Angular. With a well understanding of how to implement error handling the right way with RxJS, you are sure to run with strange problems down the line when an error does occur. By contrast, if you know what you are doing up a cover, you can remove those strange problems and save yourself some debugging distress.   Table of Content 1. Infinite Observables 2. DOM Event Case Study 3. Bad Error Handling 4. Good Error Handling 5. Output 6. NgRX Effect Case Study 7. CallWithoutError Effect 8. CallWithError Effect 9. CallWithErrorKeepListening Effect 10. CallWithErrorNotCaughtEffect 11. Conclusion In this blog, we will discuss the type of RxJS observables to be most involved, how to incorrectly handle an error through RxJS, what happens when you do not handle an error applying RxJS and how to precisely handle errors applying RxJS. Infinite Observables This blog will be linked with infinite observables, those that you hope to keep getting values from. That is because if you do error handling incorrectly, they stop to be infinite observables and finish, which will be very bad. After all, your app is expecting it to be infinite. These will be considered: DOM Event: A DOM Event ‘keyup’ that you wanna debounce as the user types on the page and then look up through an API. NgRx Effect: An NgRx Effect that you hope will forever be listening for dispatched actions DOM Event Case Study That first consideration will concentrate on handling DOM events and doing searches based on them. There will be two input boxes that you type in a character's name of Star Wars. While you stop typing for 300 milliseconds and the text is changed. It will search for those names through the Star Wars API and display results. The first input box will keep working after an error. The second input box will halt working after an error. Here is the interface: Figure: DOM Event case study interface We have gamed this a pretty bit so that if you type “error”, it will search over a wrong URL, therefore creating an error. Here is the appropriate HTML: The key up event normally emits through the “next” method of the Subject. Following is the component code:   import { Component } from '@angular/core'; import { Subject } from 'rxjs'; import { finalize } from 'rxjs/operators'; import { RxJSDemoService } from './rx-jsdemo.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'RxJSErrorHandlingDemo'; searchTerm$ = new Subject(); searchTermError$ = new Subject(); resultsError: any; results: any; constructor( private rxjsDemoService: RxJSDemoService, ) {} ngOnInit(): void { this.rxjsDemoService .searchBadCatch(this.searchTermError$) .pipe( finalize(() => console.log("searchTermError$ (bad catch) finalize called!") ) ) .subscribe(results => { console.log("Got results from search (bad catch)"); this.resultsError = results.results; }); this.rxjsDemoService .search(this.searchTerm$) .pipe(finalize(() => console.log("searchTerm$ finalize called!"))) .subscribe(results => { console.log("Got results from search (good catch)"); this.results = results.results; }); } } This code essentially will be reporting results to the page and logging either it was called or not. Note that we are calling two various service methods and passing in two several subjects. Read More: Getting Started With Angular Animations The error handling code for this case study is in the following rxjsDemoService :   import { Injectable } from '@angular/core'; import { of } from 'rxjs'; import { switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators'; import { catchError } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { HttpClient } from "@angular/common/http"; @Injectable({ providedIn: 'root' }) export class RxJSDemoService { constructor(private http: HttpClient){} searchBadCatch(terms: Observable) { return terms.pipe( debounceTime(300), distinctUntilChanged(), switchMap(term => this.searchStarWarsNames(term)), catchError(error => { console.log("Caught search error the wrong way!"); return of({ results: null }); }) ); } search(terms: Observable) { return terms.pipe( debounceTime(300), distinctUntilChanged(), switchMap(term => this.searchStarWarsNames(term).pipe( catchError(error => { console.log("Caught search error the right way!"); return of({ results: null }); }) ) ) ); } private searchStarWarsNames(term) { let url = `https://swapi.co/api/people/?search=${term}`; if (term === "error") { url = `https://swapi.co/apix/people/?search=${term}`; } return this.http.get(url); } } Bad Error Handling The searchBadCatch method has our bad error handling code. Just looking at it, it looks fine. It's debouncing for 300 ms, has the distinctUntilChanged to ensure we do not search for the same thing twice in a row. There is a switchMap that calls the searchStarWarsName method and we are catching errors through the catchError method. If you catch the error through catchError at the first step of the Observables pipe method. It will enable you to handle the error and return one more result in the stream but will then finish the observable stream. And that indicates it won't listen to key up events anymore. Thus, at all costs never allow an error to percolate to this step. Remark that if catchError is given on the initial level of the Observable pipe method, the finalize operator will be called. You can see this up in the component code. Following is the code of it: Figure: Component code Never let an error permeate to the level of the red line. Good Error Handling The search method has our RxJS finest practice error handling code: Always put the catchError operator within a switchMap so that it only ends the API call stream and then returns the stream to the switchMap, which remains the Observable. If you are not calling an API, ensure to add a try or catch block so that you can handle the error in the catch block and not allow it to permeate to the first level pipe. Do not believe your code will never fail, use a try or catch block. So, you can see in the code that we add a pipe to the searchStarWarsNames call so that within of there, we catchError and therefore not allow the error to permeate to the first level pipe. And the best practice code is the following: Figure: Best practice code Always catch errors within the switchMap/mergeMap/concatMap, etc. Output Now it is time to see how this works on the website. We can imagine that it works at first. The fun starts when an error is generated from the API call. First,we will type error in both input boxes as shown: Figure: Output We will quite it as an exercise for you to see the console output. Now for the actual test cantype in something and get results later handling the error? Here we see that the first one works and the second one does not work anymore: Figure: console output The second input box is what we were talking about over a Weird problem in the intro. You would have a difficult time estimating out why your search quit working. Looking to Hire Angular Developer ? Your Search ends here. See here NgRX Effect Case Study The reason we began writing this blog was that we had a weird problem with one of our apps that was through NgRX and Effects. See for information on effects. The interface is the following: Figure: NgRX and Effects Interface Nothing desires here: The success button calls the Star Wars API with "person/1" (Luke Skywalker) and gives the name in the output on the screen. Error-stops listening button calls the API with an incorrect URL so it generates an error message "catch is done wrong so it stops listening for the effect". Error-Don't catch error button calls the API with an incorrect URL so it generates an error message "not catching the error". Error-Keeps Listening button calls the API with an incorrect URL so it generates an error message "properly catching the error so you can click it multiple times". We will skip the HTML for this because it is just buttons calling component methods. Following is the component code:   import { Component, OnInit } from "@angular/core"; import { catchError } from "rxjs/operators"; import { of, throwError, Observable } from "rxjs"; import { Store, select } from "@ngrx/store"; import { RxjsService } from "./services/rxjs.service"; import * as fromRoot from "./store/reducers"; import { CallWithoutError, CallWithError, CallWithErrorKeepListening, CallWithErrorNotCaught, EffectReturnTest } from "./store/app.actions"; @Component({ selector: "app-root", templateUrl: "./app.component.html", styleUrls: ["./app.component.css"] }) export class AppComponent implements OnInit { title = "RxJS Playground"; name$: Observable; someString$: Observable; constructor( private rxjsService: RxjsService, private store: Store ) {} ngOnInit(): void { this.rxjsService.subject .pipe( catchError(error => { console.log("Error in catchError", error); return of(error); }) ) .subscribe( value => { console.log("Subject value:", value); }, error => console.log("Error!", error) ); this.name$ = this.store.pipe(select(fromRoot.getName)); this.someString$ = this.store.pipe(select(fromRoot.getSomeString)); } pokeSubject(value: boolean) { this.rxjsService.nextSubject(value); } errorSubject() { this.rxjsService.errorSubject(); } ngrxSuccess() { this.store.dispatch(new CallWithoutError()); } ngrxError() { this.store.dispatch(new CallWithError()); } ngrxErrorKeepListening() { this.store.dispatch(new CallWithErrorKeepListening()); } ngrxErrorDontCatch() { this.store.dispatch(new CallWithErrorNotCaught()); } ngrxEffectReturnTest(actionNum) { this.store.dispatch(new EffectReturnTest(actionNum)); } } The error handling excellent, evil, and ugly is in the effected code. CallWithoutError Effect Following is our success case: This individual will work each time but if it failed, it would remain working because the catchError is inside the http.get pipe. For the resolution case, the SetName reducer will add the “name” to the store. The UI plucks that up and displays it. CallWithError Effect This effect will call the API with the wrong URL so an error is caused. The error handling is done wrongly so once called, this will never work repeatedly unless the app is refreshed. In this case, the catchError at the first level of this action$.pipe will get called, thus ending the effect since its Observable stream will end. This is much like in the case study above through many RxJS Observables. We should see the message "Error-You're Fasted" on the page after clicking it. If we try clicking the button return, it will not fire the effect. Following is the output for this: Figure: Output of callWithoutError Effect CallWithErrorKeepListening Effect This effect will call the API through the incorrect URL so an error is generated. Despite that, it will handle the error properly so that it can be called again. The correct way to handle the RxJS error is by putting the catchError within the http.get pipe. It will end the http.get observable but that does not matter because it's a finite observable anyways and only emits one value. While it returns the SetName action, the switchMap will release it and remain the Observable stream. Notice that the finalize there will never be called. Following is the output of this: Figure: Output of callWithErrorListening Effect CallWithErrorNotCaughtEffect This our last effect that describes what happens if we do not catch the error? It behaves the same way as if we handled the error incorrectly. It is just that you are not looking into that error stream. Also, the name on the UI will not be set after you aren't calling setName in the catchError operator. So, you will moreover not see any output if it was the first button clicked or you will see the last name that was set. Another mysterious problem that would be difficult to debug. Conclusion As you can tell from this blog, knowing how to appropriately handle RxJS errors in your Angular app will help you to prevent those weird problems you could see when your infinite Observable stream ends unexpectedly. Through this knowledge, you should be able to make sure that your infinite observables never and unless you decide they are finished.

Build Your Agile Team

Enter your e-mail address Please enter valid e-mail

Categories

Ensure your sustainable growth with our team

Talk to our experts
Sustainable
Sustainable
 

Blog Our insights

Power Apps vs Power Automate: When to Use What?
Power Apps vs Power Automate: When to Use What?

I often see people asking questions like “Is Power App the same as Power Automate?”. “Are they interchangeable or have their own purpose?”. We first need to clear up this confusion...

Is It Worth Using Azure With Power Platforms For Financial Business?
Is It Worth Using Azure With Power Platforms For Financial Business?

The era of traditional software development is fading; Azure Cloud and Power Platform services are taking charge to run businesses of the new age. When it comes to Financial business,...

Microsoft Power BI And PowerApps Development: Transforming The Healthcare Sector
Microsoft Power BI And PowerApps Development: Transforming The Healthcare Sector

If someone had told you just a few years ago that you’d soon develop mobile apps without writing a single line of code, you might not have believed them. Yes, or no? Today, over 7...