Bugs, RxJS & Angular
What attracts experts in Angular and depresses beginners? Same thing, RxJS.
Why is it so difficult for beginners? One of the reasons is that there are a huge number of operators that you just need to know, and without searching, understand what is the difference between concatMap, switchMap and mergeMap. Why is it so popular with those who have already studied it? Because you begin to understand the full power of RxJS, when with a couple of statements you can do what you would write in imperative code for half a day on two pages. After all, it’s so nice to feel like a god when the code just bounces off your fingers, and you happily tell your colleagues how cool you are and, most importantly, just solved the problem.
At the same time, everyone remembers that calling multiple subscribe is bad (as well as subscribing in general), calling side effects from tap (although of course everyone does this). And what is most often forgotten with RxJS? Mistakes. Which mistakes? Personally, I distinguish only two types expected and unexpected.
Unexpected errors – these are the errors that occur as a result of bad code or an unexpected JSON response from the server – division by zero, access to a null object, incorrect JSON parsing. Many examples can be given, the main thing is that these are full-fledged errors that require changes in your code.
Expected mistakes – these are errors that we can foresee in the operation of the application, the simplest example is a fallen server with data. Here we, as programmers, do not correct anything in our code, but usually we go to swear in another command.
These types of errors, I think, should be separated in the consequences – in the case of the first, it’s beautiful to fall, in the case of the second, to be able to work with them and show our users something from which we can make a beautiful screenshot “they are to blame, not us”. How to do it? By using a representation of the data that by itself will show you that it can contain both data and an error. Meet
In general, this is a union type, on the left – an error, on the right – correct data.
However, I propose not to use the self-written type, but to take it ready. Many take the type from the library fp-tsbut I propose to take a more lightweight and understandable @sweet-monads/either.
Let’s create a simple Angular application that will load the data of a github user, and then (not) beautifully crash when trying to load a non-existent one on button clicks.
The code accordingly looks like this
It’s a fairly simple application that crashes when we try to do something we haven’t thought of (in this case, loading a non-existent user). Sadly, visually, this will look like a broken button at best, at worst, everything that was based on such code will also fall.
There are many attempts to solve errors using RxJS methods, I suggest studying in the excellent article RxJs Error Handling: Complete Practical Guide
In Angular, I most often saw a solution through the definition of separate data and error fields in the component model.
Why this (as well as other solutions) is bad, I suggest watching in a wonderful video from Artyom Kobzar and Dmitry Makhnev The (un|necessary) monad Either in practice and in theory
Well, in reality, we just forget about it until it falls, and we solve such problems pointwise. I propose to initially consider errors as full-fledged residents of our city and never trust the backend, even if we are happy owners of the FullStack Developer title.
First, let’s write a simple RxJS statement that will automatically catch errors and convert them into an error structure. For simplicity, let’s save only the text of the error.
What is the beauty of such an operator as a separate unit in the application – that we can apply it pointwise, in separate places of the application, and not migrate entirely.
The code using this operator will look like this
It is better to show data in html through the creation of special directives and wrapper components (it is also in the alpha version available in npm as a package) I will not give them here, I propose to study the updated example immediately, including the code, by this link.
The main changes are the appearance of the Either component and the use of right data through the ifRight structural directive.
Now, when it crashes, we will show the text of the error, and in the case of loaded data, we will work with them.
As you can see, here we are sure that the error will be handled more in the future, if we need it, we can always, for example, pledge both using the mapEither, switchMapEither operators (there is in the example)