Angular RxJS Error Handling
RxJS Error Handling – RxJS is a big part of Angular. Without a good understanding of how to do error handling the right way with RxJS, you are sure to run into an unexpected problems down the line when an error does occur. In contrast, if you know what you are doing up front, you can eliminate those unexpected problems and save yourself some debugging pain.
This RxJS article will examine:
The code for this article is available on github.
Infinite Observables
This article will be dealing with infinite observables – those that you expect to keep getting values from. That’s because if you do error handling wrong, they cease to be infinite observables and finish – which will be very bad since your application is expecting it to be infinite.
These will be studied:
DOM Event
Case Study
The first case study will focus on handling DOM events and doing searches based off of them. There will be two input boxes that you type in a Star Wars character’s name. When you stop typing for 300 milliseconds and the text is different than the last one, it will search for those names using the Star Wars API and display the results. The first input box will continue to work after an error. The second input box will stop working after an error.
The Interface:
The relevant HTML:
PGlucHV0IGNsYXNzPSJmb3JtLWNvbnRyb2wiIChrZXl1cCk9InNlYXJjaFRlcm0kLm5leHQoJGV2ZW50LnRhcmdldC52YWx1ZSkiIC8+Cgo8aW5wdXQgY2xhc3M9ImZvcm0tY29udHJvbCIgKGtleXVwKT0ic2VhcmNoVGVybUVycm9yJC5uZXh0KCRldmVudC50YXJnZXQudmFsdWUpIiAvPg==
The component code:
c2VhcmNoVGVybSQgPSBuZXcgU3ViamVjdDxzdHJpbmc+KCk7CiAgICBzZWFyY2hUZXJtRXJyb3IkID0gbmV3IFN1YmplY3Q8c3RyaW5nPigpOwoKICAgIHRoaXMucnhqc1NlcnZpY2UKICAgICAgLnNlYXJjaEJhZENhdGNoKHRoaXMuc2VhcmNoVGVybUVycm9yJCkKICAgICAgLnBpcGUoCiAgICAgICAgZmluYWxpemUoKCkgPT4KICAgICAgICAgIGNvbnNvbGUubG9nKCJzZWFyY2hUZXJtRXJyb3IkIChiYWQgY2F0Y2gpIGZpbmFsaXplIGNhbGxlZCEiKQogICAgICAgICkKICAgICAgKQogICAgICAuc3Vic2NyaWJlKHJlc3VsdHMgPT4gewogICAgICAgIGNvbnNvbGUubG9nKCJHb3QgcmVzdWx0cyBmcm9tIHNlYXJjaCAoYmFkIGNhdGNoKSIpOwogICAgICAgIHRoaXMucmVzdWx0c0Vycm9yID0gcmVzdWx0cy5yZXN1bHRzOwogICAgICB9KTsKCiAgICB0aGlzLnJ4anNTZXJ2aWNlCiAgICAgIC5zZWFyY2godGhpcy5zZWFyY2hUZXJtJCkKICAgICAgLnBpcGUoZmluYWxpemUoKCkgPT4gY29uc29sZS5sb2coInNlYXJjaFRlcm0kIGZpbmFsaXplIGNhbGxlZCEiKSkpCiAgICAgIC5zdWJzY3JpYmUocmVzdWx0cyA9PiB7CiAgICAgICAgY29uc29sZS5sb2coIkdvdCByZXN1bHRzIGZyb20gc2VhcmNoIChnb29kIGNhdGNoKSIpOwogICAgICAgIHRoaXMucmVzdWx0cyA9IHJlc3VsdHMucmVzdWx0czsKICAgICAgfSk7
The error handling code for this case study is in the rxjsService:
IHNlYXJjaEJhZENhdGNoKHRlcm1zOiBPYnNlcnZhYmxlPHN0cmluZz4pIHsKICAgIHJldHVybiB0ZXJtcy5waXBlKAogICAgICBkZWJvdW5jZVRpbWUoMzAwKSwKICAgICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKSwKICAgICAgc3dpdGNoTWFwKHRlcm0gPT4gdGhpcy5zZWFyY2hTdGFyV2Fyc05hbWVzKHRlcm0pKSwKICAgICAgY2F0Y2hFcnJvcihlcnJvciA9PiB7CiAgICAgICAgY29uc29sZS5sb2coIkNhdWdodCBzZWFyY2ggZXJyb3IgdGhlIHdyb25nIHdheSEiKTsKICAgICAgICByZXR1cm4gb2YoeyByZXN1bHRzOiBudWxsIH0pOwogICAgICB9KQogICAgKTsKICB9CgogIHNlYXJjaCh0ZXJtczogT2JzZXJ2YWJsZTxzdHJpbmc+KSB7CiAgICByZXR1cm4gdGVybXMucGlwZSgKICAgICAgZGVib3VuY2VUaW1lKDMwMCksCiAgICAgIGRpc3RpbmN0VW50aWxDaGFuZ2VkKCksCiAgICAgIHN3aXRjaE1hcCh0ZXJtID0+CiAgICAgICAgdGhpcy5zZWFyY2hTdGFyV2Fyc05hbWVzKHRlcm0pLnBpcGUoCiAgICAgICAgICBjYXRjaEVycm9yKGVycm9yID0+IHsKICAgICAgICAgICAgY29uc29sZS5sb2coIkNhdWdodCBzZWFyY2ggZXJyb3IgdGhlIHJpZ2h0IHdheSEiKTsKICAgICAgICAgICAgcmV0dXJuIG9mKHsgcmVzdWx0czogbnVsbCB9KTsKICAgICAgICAgIH0pCiAgICAgICAgKQogICAgICApCiAgICApOwogIH0KCiAgcHJpdmF0ZSBzZWFyY2hTdGFyV2Fyc05hbWVzKHRlcm0pIHsKICAgIGxldCB1cmwgPSBgaHR0cHM6Ly9zd2FwaS5jby9hcGkvcGVvcGxlLz9zZWFyY2g9JHt0ZXJtfWA7CiAgICBpZiAodGVybSA9PT0gImVycm9yIikgewogICAgICB1cmwgPSBgaHR0cHM6Ly9zd2FwaS5jby9hcGl4L3Blb3BsZS8/c2VhcmNoPSR7dGVybX1gOwogICAgfQoKICAgIHJldHVybiB0aGlzLmh0dHAuZ2V0PGFueT4odXJsKTsKICB9
Bad Error Handling
SearchBadCatch
The “searchBadCatch” method has our bad error handling code. Just looking at it, it looks fine, right? It is debouncing for 300 ms, has the distinctUntilChanged to make sure we don’t search for the same thing twice in a row. There is a switchMap that calls the “searchStarWarsNames” method and we are catching errors using the catchError method. What’s wrong with it?
If you catch the error using “catchError” at the first level of the Observables “pipe” method (in this case return terms.pipe()), it will allow you to handle the error and return one more result in the stream but will then end the observable stream. And that means it won’t listen to “keyup” events anymore. Therefore, at all cost, never allow an error to percolate to this level.
Good Error Handling
“Search” Method
The “search” method has our RxJS best practice error handling code:
Always put the “catchError” operator inside a switchMap (or similar) so that it only ends the API call stream and then returns the stream to the switchMap, which continues the Observable. If you are not calling an API, make sure to add a try/catch block so that you can handle the error in the catch block and not allow it to percolate to the first level “pipe”. Don’t assume your code will “never fail”, use a try/catch block.
Output
First, I’ll type “error” in both input boxes as shown:
I’ll leave it as an exercise for you to see the console output. Now for the real test, can I type in something and get results after handling the error?
The first one works but the second one doesn’t anymore:
The second input box is what I was talking about with a “unexpected problem” in the introduction. You would have a tough time figuring out why your search quit working.
NgRx Effect
Case Study
The Interface:
Nothing fancy here:
- “Success” – calls the Star Wars API with “person/1” (Luke Skywalker) and outputs the name on the screen
- “Error – Stops Listening” – calls the API with a wrong URL so it generates an error – the catch is done wrong so it stops listening for the effect
- “Error – Don’t catch error” – calls the API with a wrong URL so it generates an error – not catching the error
- “Error – Keeps Listening” – calls the API with a wrong URL so it generates an error – properly catching the error so you can click it multiple times
I’ll skip the HTML for this since it’s just buttons calling component methods. Here is the component code:
bmdyeFN1Y2Nlc3MoKSB7CiAgICB0aGlzLnN0b3JlLmRpc3BhdGNoKG5ldyBDYWxsV2l0aG91dEVycm9yKCkpOwogIH0KCiAgbmdyeEVycm9yKCkgewogICAgdGhpcy5zdG9yZS5kaXNwYXRjaChuZXcgQ2FsbFdpdGhFcnJvcigpKTsKICB9CgogIG5ncnhFcnJvcktlZXBMaXN0ZW5pbmcoKSB7CiAgICB0aGlzLnN0b3JlLmRpc3BhdGNoKG5ldyBDYWxsV2l0aEVycm9yS2VlcExpc3RlbmluZygpKTsKICB9CgogIG5ncnhFcnJvckRvbnRDYXRjaCgpIHsKICAgIHRoaXMuc3RvcmUuZGlzcGF0Y2gobmV3IENhbGxXaXRoRXJyb3JOb3RDYXVnaHQoKSk7CiAgfQ==
CallWithoutError Effect
Success Case
This is our success case:
QEVmZmVjdCgpCiAgY2FsbFdpdGhvdXRFcnJvciQgPSB0aGlzLmFjdGlvbnMkLnBpcGUoCiAgICBvZlR5cGUoQXBwQWN0aW9uVHlwZXMuQ2FsbFdpdGhvdXRFcnJvciksCiAgICBzd2l0Y2hNYXAoKCkgPT4gewogICAgICBjb25zb2xlLmxvZygiQ2FsbGluZyBhcGkgd2l0aG91dCBlcnJvciIpOwoKICAgICAgcmV0dXJuIHRoaXMuaHR0cC5nZXQ8YW55PihgPGEgY2xhc3M9InZnbG5rIiBocmVmPSJodHRwczovL3N3YXBpLmNvL2FwaS9wZW9wbGUvMSIgcmVsPSJub2ZvbGxvdyI+PHNwYW4+aHR0cHM8L3NwYW4+PHNwYW4+Oi8vPC9zcGFuPjxzcGFuPnN3YXBpPC9zcGFuPjxzcGFuPi48L3NwYW4+PHNwYW4+Y288L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5hcGk8L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5wZW9wbGU8L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj4xPC9zcGFuPjwvYT5gKS5waXBlKAogICAgICAgIG1hcChyZXN1bHRzID0+IHJlc3VsdHMubmFtZSksCiAgICAgICAgc3dpdGNoTWFwKG5hbWUgPT4gb2YobmV3IFNldE5hbWUobmFtZSkpKSwKICAgICAgICBjYXRjaEVycm9yKGVycm9yID0+IG9mKG5ldyBTZXROYW1lKCJFcnJvciEiKSkpCiAgICAgICk7CiAgICB9KSwKICAgIGZpbmFsaXplKCgpID0+IGNvbnNvbGUubG9nKCJDYWxsV2l0aG91dEVycm9yIGZpbmFsaXplIGNhbGxlZCEiKSkKICApOw==
CallWithError Effect
Wrong URL
QEVmZmVjdCgpCiAgY2FsbFdpdGhFcnJvciQgPSB0aGlzLmFjdGlvbnMkLnBpcGUoCiAgICBvZlR5cGUoQXBwQWN0aW9uVHlwZXMuQ2FsbFdpdGhFcnJvciksCiAgICBzd2l0Y2hNYXAoKCkgPT4gewogICAgICBjb25zb2xlLmxvZygiQ2FsbGluZyBhcGkgd2l0aCBlcnJvciAtIHN0b3AgbGlzdGVuaW5nIik7CgogICAgICByZXR1cm4gdGhpcy5odHRwLmdldDxhbnk+KGA8YSBjbGFzcz0idmdsbmsiIGhyZWY9Imh0dHBzOi8vc3dhcGkuY28vYXBpeC9wZW9wbGUvMSIgcmVsPSJub2ZvbGxvdyI+PHNwYW4+aHR0cHM8L3NwYW4+PHNwYW4+Oi8vPC9zcGFuPjxzcGFuPnN3YXBpPC9zcGFuPjxzcGFuPi48L3NwYW4+PHNwYW4+Y288L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5hcGl4PC9zcGFuPjxzcGFuPi88L3NwYW4+PHNwYW4+cGVvcGxlPC9zcGFuPjxzcGFuPi88L3NwYW4+PHNwYW4+MTwvc3Bhbj48L2E+YCkucGlwZSgKICAgICAgICBtYXAocmVzdWx0cyA9PiByZXN1bHRzLm5hbWUpLAogICAgICAgIHN3aXRjaE1hcChuYW1lID0+IG9mKG5ldyBTZXROYW1lKG5hbWUpKSkKICAgICAgKTsKICAgIH0pLAogICAgY2F0Y2hFcnJvcihlcnJvciA9PiBvZihuZXcgU2V0TmFtZSgiRXJyb3IgLSBZb3UncmUgZG9vbWVkISIpKSksCiAgICBmaW5hbGl6ZSgoKSA9PiBjb25zb2xlLmxvZygiQ2FsbFdpdGhFcnJvciBmaW5hbGl6ZSBjYWxsZWQhIikpCiAgKTs=
In this case, the “catchError” at the first level of the this.actions$.pipe will get called, thus ending the effect because its Observable stream will end. This is just like in the case study above using just RxJS Observables. We should see “Error – You’re doomed!” on the page after clicking it. If we try clicking that button again, it will not fire the effect.
The output for this:
CallWithErrorKeep-Listening Effect
Wrong URL Handled Properly
QEVmZmVjdCgpCiAgY2FsbFdpdGhFcnJvcktlZXBMaXN0ZW5pbmckID0gdGhpcy5hY3Rpb25zJC5waXBlKAogICAgb2ZUeXBlKEFwcEFjdGlvblR5cGVzLkNhbGxXaXRoRXJyb3JLZWVwTGlzdGVuaW5nKSwKICAgIHN3aXRjaE1hcCgoKSA9PiB7CiAgICAgIGNvbnNvbGUubG9nKCJDYWxsaW5nIGFwaSB3aXRoIGVycm9yIC0ga2VlcCBsaXN0ZW5pbmciKTsKCiAgICAgIHJldHVybiB0aGlzLmh0dHAuZ2V0PGFueT4oYDxhIGNsYXNzPSJ2Z2xuayIgaHJlZj0iaHR0cHM6Ly9zd2FwaS5jby9hcGl4L3Blb3BsZS8xIiByZWw9Im5vZm9sbG93Ij48c3Bhbj5odHRwczwvc3Bhbj48c3Bhbj46Ly88L3NwYW4+PHNwYW4+c3dhcGk8L3NwYW4+PHNwYW4+Ljwvc3Bhbj48c3Bhbj5jbzwvc3Bhbj48c3Bhbj4vPC9zcGFuPjxzcGFuPmFwaXg8L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5wZW9wbGU8L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj4xPC9zcGFuPjwvYT5gKS5waXBlKAogICAgICAgIG1hcChyZXN1bHRzID0+IHJlc3VsdHMubmFtZSksCiAgICAgICAgc3dpdGNoTWFwKG5hbWUgPT4gb2YobmV3IFNldE5hbWUobmFtZSkpKSwKICAgICAgICBjYXRjaEVycm9yKGVycm9yID0+IG9mKG5ldyBTZXROYW1lKCJFcnJvciBidXQgc3RpbGwgbGlzdGVuaW5nISIpKSkKICAgICAgKTsKICAgIH0pLAogICAgZmluYWxpemUoKCkgPT4gY29uc29sZS5sb2coIkNhbGxXaXRoRXJyb3JLZWVwTGlzdGVuaW5nIGZpbmFsaXplIGNhbGxlZCEiKSkKICApOw==
The right way to handle the RxJS error is by putting the “catchError” inside the http.get “pipe”. It will end the http.get observable but that doesn’t matter because it is a finite observable anyways and only emits one value. When it returns the SetName action, the switchMap will emit it and continue the Observable stream. Note that the finalize here will never be called.
The output for this:
CallWithError-NotCaught Effect
When You Don’t Catch Error
QEVmZmVjdCgpCiAgY2FsbFdpdGhFcnJvckRvbnRDYXRjaCQgPSB0aGlzLmFjdGlvbnMkLnBpcGUoCiAgICBvZlR5cGUoQXBwQWN0aW9uVHlwZXMuQ2FsbFdpdGhFcnJvck5vdENhdWdodCksCiAgICBzd2l0Y2hNYXAoKCkgPT4gewogICAgICBjb25zb2xlLmxvZygiQ2FsbGluZyBhcGkgd2l0aCBlcnJvciAtIGRvbid0IGNhdGNoIik7CgogICAgICByZXR1cm4gdGhpcy5odHRwLmdldDxhbnk+KGA8YSBjbGFzcz0idmdsbmsiIGhyZWY9Imh0dHBzOi8vc3dhcGkuY28vYXBpeC9wZW9wbGUvMSIgcmVsPSJub2ZvbGxvdyI+PHNwYW4+aHR0cHM8L3NwYW4+PHNwYW4+Oi8vPC9zcGFuPjxzcGFuPnN3YXBpPC9zcGFuPjxzcGFuPi48L3NwYW4+PHNwYW4+Y288L3NwYW4+PHNwYW4+Lzwvc3Bhbj48c3Bhbj5hcGl4PC9zcGFuPjxzcGFuPi88L3NwYW4+PHNwYW4+cGVvcGxlPC9zcGFuPjxzcGFuPi88L3NwYW4+PHNwYW4+MTwvc3Bhbj48L2E+YCkucGlwZSgKICAgICAgICBtYXAocmVzdWx0cyA9PiByZXN1bHRzLm5hbWUpLAogICAgICAgIHN3aXRjaE1hcChuYW1lID0+IG9mKG5ldyBTZXROYW1lKG5hbWUpKSkKICAgICAgKTsKICAgIH0pLAogICAgZmluYWxpemUoKCkgPT4gY29uc29sZS5sb2coIkNhbGxXaXRoRXJyb3JOb3RDYXVnaHQgZmluYWxpemUgY2FsbGVkISIpKQogICk7
Conclusion
Know How To Properly Handle RxJS Errors In Your Angular Application using Angular RxJS Error Handling
Java is making strides toward modernity. For one thing, As you can tell from this article, knowing how to properly handle RxJS errors in your Angular application will help you to prevent those “weird problems” you could see when your infinite Observable stream ends unexpectedly. Using this knowledge, you should be able to ensure that your infinite Observables never end until you decide they are finished.
Rich Franzmeier
Consultant
Rich Franzmeier is a senior developer at Intertech and author of several blog posts on topics ranging from Angular to NgRx to Javascript.
“Whether you need help implementing Agile or need to boost your capabilities with a team of software developers that have proven expertise in design and development, no matter the technology, our proven consulting services can help you succeed the first time.”
Consulting Services
Consider introducing Intertech’s experience into your project and you will realize a higher degree of success around project goals, deliverables, timelines, and budgets.
Training Services
Consider Intertech for your companies training needs! A primary provider of training for fortune 5 to 5000 companies, Intertech brings a (1) proven resource that (2) knows how to plug into your system in a way that (3) accents your internal university system and makes you look great.