DATEV Nine-Nine | Create your own Operator — RxJS Edition
By: Matthias Alt, Stephan Bierwirth & Gerrit Riesch
If you’re using Angular, then you’re also using RxJS. In our previous blog post we provided you with some tips on how to remember the different types of Flattening Operators. But RxJS has a lot more to offer than just Flattening Operators (a subtype of Transformation Operators). There are Creation Operators, Transformation Operators, Filtering Operators, Join Operators… and a lot more. And on top of that it is also possible to extend the default Operators set by creating your own operators. Creating your own Operators sounds more complicated than it actually is and, in this blog, we want to show you the benefits of custom Operators and how to DIY.
What exactly are RxJS Operators? Simply put, Operators are functions and can be pipeable or creation Operators i.e., they are either pure functions with an observable input and output or they create a new Observable from scratch. In our basic example we use a creation operator to generate numbers of precincts and a pipeable operator to filter for certain precinct values. Finally, we subscribe and log the results. In this example we are using only default RxJS Operators. But what if we need this filter operator on multiple occasions? And what if we have very complex behavior in our pipe? Well, maybe a custom Operator is in order then.
Creating Custom Operators
To demonstrate the creation and usage of a custom operator we prepared a simple case in the universe of a certain famous Brooklyn precinct for you.
We have a list of detective reports, which include an id, a case, detectives, and a status to check if the case is solved or not.
Now we want to filter all solved cases and return only the ids of those, like in the following example.
With a custom operator we can encapsulate the operations on the observable and use the operator directly.
Nine-Nine Tip: Pluck Operator Deprecation
The Operator pluck(‘foo’) was a shorthand for map(x => x.foo), with the advantage of shortness and path traversal safety. With optional chaining this holds no longer true and pluck has been deprecated in favor of map with optional chaining: pluck(‘foo’, ‘bar’) is map(x => x?.foo?.bar). Pluck will be removed in RxJS v8. For more information checkout the official docs for RxJS — pluck.
Testing Custom Operators
You can simply test your custom Operators with marble tests. Marble tests are very common if you want to verify the behavior of Operations on observables. You can see it as a kind of black box testing for observables. You only have to describe your input observable and your expected output observable.
For our custom operator we have the following marble test.
Benefits of Custom Operators
If you are using certain sequences of RxJS Operators frequently it can be very useful and DRY to extract them into a dedicated Operator. Even if you aren’t using the Operator in multiple places, it can still increase readability and support the notion of “self-documenting” code if you extract logically coherent blocks into custom Operators. Another advantage is that it is a lot easier to unit test small Operators one by one instead of having them all in a huge chain. Therefore, we recommend not to shy away from extracting custom Operators, just like you would with “normal” functions if it improves code quality.
In this article we showed you how simple it is to create your own RxJS Operator, how to test it and why you should even consider creating your own Operators. It also helps in understanding RxJS a little more and without any hesitation we recommend giving it a try!