Angular Observable Stores— or How to Pass Info Between Components, With Encapsulation.
By: Justin Miles
Tags: Observables, JavaScript, Typescript, Web Development, Angular

*This article assumes a basic familiarity with rxjs observables.*

Last week I saw a post on dev.to by Nicolas Larrode titled ‘Communicate between Angular components.’ In the article, he detailed a simple service that allowed data to be passed between components by using a service that exposed a Subject.

If you’re not familiar with a Subject, just think of it as a two-way Observable. You can subscribe to it, sure, but you can also push values to all of its subscribers. Like so:

So in the article, Nicolas describes how we can use a subject in a service as a way to pass items between components. We do this by having both components inject the service, which is a singleton, meaning they both have the SAME instance of the service.

The service contains a Subject which is public. Component A can subscribe to it just as it would an observable. Component B can then call the next method on the subject, passing the value it wants Component A to be aware of.

I created a stackblitz to show the whole thing in action:

As it stands, this code is definitely useful. But it has some issues from an OOP perspective. We have our Subject hanging out where anyone can just push whatever they want by calling the next method, and when they do every subscriber reacts. This means that each of our subscribers needs to be aware of what all the others are doing at all times, so they don’t get caught off-guard with an unexpected value.

Take the example from the stackblitz above. We have two sibling components subscribed to the DataService. Both take whatever greeting the service provides and dutifully repeat it to their sibling. If you enter “Hello”, in the textbox and submit, they say: ‘Hello Brother!’ and ‘Hello Sister!’. What if sister wanted to cause some trouble though, she could call this code:

dataService.next("Shut up");

this.greeting = "Please don't say that, I love you ";

See the problem here? Sister is able to make brother say whatever she wants.

Say we want to prevent such foul language from being used between siblings. All we’d have to do is go into brother’s code and scrub the output, so that if the message was “shut up” we replace it with “I love you.” Something like this:

if(greeting.toLowerCase() === 'shut up'){

filteredGreeting = 'Tell me more';

}

Simple, now brother will never say shut up again. We probably need to copy that same code over to sister too.

I don’t like this.

Here’s how we fix it:

First, a brief aside. I like to use a BehaviorSubject for this. It’s a lot like a Subject, with one important difference. When an object subscribes, they don’t have to wait for a new value to be emitted, they get the last value that was emitted. Which is nice for storing state in our app.

So as before, we’ll have a couple of components and a service. The sibling components will subscribe to the ‘data’ being exposed by the service. From their perspective, not much changes.

The service is where the magic happens. We’re going from this:

To this:

A few things changed. The first thing you’ll notice is this line:

private _data: BehaviorSubject<string> = new BehaviorSubject('');

We made a private BehaviorSubject. No components can push data to it, they can’t even subscribe to it. We also had to pass an initial value to its constructor. Since subscribers get the last value immediately after signing up, you have to give an initial value, even if it is empty.

public get Data(): Observable<string>{ return this._data.asObservable(); }

Secondly, we created a public property to expose only the observable portion of our BehaviorSubject. This is what components will see. Just an observable they can subscribe to. If they want to change the greeting, they have to call SetGreeting.

SetGreeting(greeting: string){

this._data.next(filteredGreeting);

}

Here we push to our private variable, which is seen by our components that have subscribed to the observable portion. This method is where we could put the filtering logic I mentioned earlier.

We now have a service that provides a view of our data, but is much more loosely coupled to its subscribers than the previous implimentation.

See the ‘before’ code here: https://stackblitz.com/edit/angular-itfpzv

and the ‘after’ here: https://stackblitz.com/edit/angular-qjrdry

I hope this helps you when you need a simple store. If you have complex state, consider ngrx. I’ll do another article on that soon.