Managing state in Angular 2 using RxJs
As an application grows, state management becomes more and more complex, with fragments of information scattered across many components, interacting with one another.
In Angular 1, there wasn’t a clear-cut answer as to how to make the different parts of an application talk to each other and, since the data could flow up and down, it was hard to determine which components could affect which values. A change in one place could trigger further changes, thus keeping the digest cycle going.
Angular 2, on the other hand, doesn’t have a generic mechanism implementing two-ways data binding. And it’s a good thing. Our data flow becomes much more explicit, our apps become Immutable/Observable friendly.
A thorough overview of the library would go well beyond the scope of this article. Angular 2 uses it and, if you ever used the Http class, so did you. Think about it as the lovechild of Promises on steroids and Lodash.
Let’s try to make sense of it with a simple example. In the previous article, we implemented a Search function. It was pretty straightforward.
Every time a key was entered, we would fire a function, which would query the API and return some data. It worked, it was nice, but if we ever wanted to use it somewhere else, we’d have to copy and paste our code. Ideally, we’d want to keep the behavior separated from the component.
Our first directive?
In the first article, we created some components. Some were smart and would pass data to dumb components, that’d display it.
Notice how the child component is called a directive? That’s because, in Angular 2, everything is a directive. Components just happen to be directives with a view. Let’s create a directive that will only return data.
The selector part is pretty easy to understand. Our directive will match the text inputs which have an autosearch element. Now we need a way to tell the outside world about whatever happens in our directive. It’d be pointless otherwise. It will therefore output a custom event, which we’ll call results.
Fair enough. Let’s talk business logic now.
Let’s think about the user input as a collection of values that arrives over time, an observable. We will map and filter those values and use them to query the API.
As we saw in the previous article, the service returns an observable. Since our stream is now an observable of an observable, we have to flatten them into a single entity (mergeAll) and subscribe to it.
Now, if we have a collection of values over time in our directive, we will have another collection of values going out of the directive over time, through Angular’s EventEmitter
In our component, all we now have to do is listen to the results custom event and get the data.
So, about the state…
Between Angular 1 and 2, Flux happened. If you’ve never used one of its implementations (Redux, Reflux, Alt, …), the idea behind them is simple : data moves in one direction. Let’s see how we can achieve something similar in Angular2
What we’re building
In the previous article, we set up a little application that would query the Echonest API, just to get our feet wet. Here, we’re going to add a new feature, a Favourite list.
The user can mark artists as favourites. When he does, the artist is considered as a “new” favourite. The number of new favourite artists is displayed in the header. If the user navigates to the favourite page, all the artists in the list are marked as “seen”. If the user returns to the page of an artist that he previously liked, it’s already shown as “favourite”, and the user can remove him from the list.
Enter RxJS (for real)
In our directive, we thought about the user input as a collection of values over time. A stream. In RxJS, everything can be thought of as a stream. Adding, deleting an artist from the list can be represented as a stream of updates that will act on the list, which itself will hold values over time.
Observables are good at emitting values. Observers are good at receiving them. A Subject inherits from both observable and observer. It can receive a value (A new artist to add to the collection) and emit a value (A function to add the artist in the collection).
Step by Step
Say we want to add an artist.
So far, so good. We just call a method with some data.
This is getting interesting. Remember how addFav is both observable and observer? With the next method, we supply it with new information.
Here, we don’t alter the list of favourites directly. Instead, we return a function that will do it for us. If we didn’t, we would have to say our favourites to concat whatever data it’d receive. We wouldn’t be able to update or delete artists.
We now can scan (which pretty much works like reduce array method) our updates stream, to apply the function to the previous state and return a new state.
Getting the list of the artists that are favourites is just a matter of listening to the stream, as we would do with our Echonest service.
Similarly, we can get, in another component, the number of new favourite artists by listening and filtering the same stream.
Since Angular 2 comes coupled with RxJS, I thought that it’d be fun to see how we could use it to pass data between components. It borrows some ideas from Flux/Redux, some others from Cycle.js, but without the intent to be either of them.
It is just one way to do it, and it may make more sense if you have an highly dynamic UI than in some other situation. The angular 2 docs are still a work-in-progress and I used the latest (at the time) version of RxJS, which itself is also in its early alpha stage, so there’s room for fine-tuning.
I also tried to keep the word Immutable away. I think that it’s great, but people tend to consider the matter as black or white, everything is immutable vs everything is mutable and it bores me to no end. I think both will have their place in Angular 2.
Finally, if you’ve never used RxJS before, I can’t recommend André Staltz’s words on the matter highly enough.