Using RxJS to watch for images

It's no good having our application be capable of choosing images through the dialog, or fetching them from the server during the load process, if we can't display them in the page body. As our application has features that are loosely related to each other, we don't want to introduce events as the mechanism for controlling when these happen, since this introduces tight coupling between things such as our page body component and the loading service.

What we need are services that sit between the code that handles the interaction (such as loading the data) and the page body, and passes notifications from one side to the other when something interesting has happened. The mechanism that Angular provides to do this is called Reactive Extensions for JavaScript (RxJS).

Reactive extensions are an implementation of something called the observer pattern (there's that word pattern again). This is a simple pattern to understand and you will have been using it for a while now, possibly without even recognizing it. The idea with the observer pattern is that we have a class that has something called a Subject type. Internally, this Subject type maintains a list of dependencies and, when it needs to do so, notifies those dependencies that they need to react, potentially passing the state over that they need to react to.

This might ring a vague bell that this is precisely what events do, so why should we concern ourselves with this pattern? You would be correct in your understanding—events are just a very specialist form of the observer pattern, but they have some weaknesses that things such as RxJS are designed to overcome. Suppose we had a real-time stock trading application where we had tens of thousands of stock ticks coming to our client every second. Obviously, we wouldn't want our client to handle all of those ticks, so we would have to write code inside our event handlers to start filtering out notifications. That's a lot of code that we would have to write, which would potentially be duplicated across different events. There also has to be a tight relationship between classes when we use events, so one class has to know about another in order to hook up to an event.

As our applications get bigger and more complex, there could be a lot of distance between the class that brings in the stock tick and the one that displays it. Therefore, we would end up building a complex hierarchy of events, where class A listens to an event on class B and when class B raises that event, it has to re-raise it so that class C can react to it. The more distributed our code becomes internally, the less we want to encourage this tight coupling.

With libraries such as RxJS, we solve these issues (and many more) by decoupling away from events. With RxJS, we can make sophisticated subscription mechanisms, where we can do things such as throttling the number of notifications we react to or choosing only to subscribe to data and changes where certain conditions are met. As new components are added at runtime, they can query the observable class to see what values are already available, in order to prepopulate the screen with data that has already been received. These features are more than we need in this application, but as we will use them in future chapters, it makes sense for us to be aware that they are available to us.

Our application has two things we need to react to:

  • When the page is loaded, the images will be loaded from the server, so we need to react to each image being loaded in
  • When the user chooses an image from the dialog, after the dialog has closed because the user chose Save, we need to trigger a save to the database, and also display the image on the page

It's probably not going to come as a surprise to learn that we are going to create services to satisfy these two requirements. As they both internally do the same thing, the only difference is what the subscriber needs to do once it reacts. We start off by creating a simple base class that these services will derive from:

export class ContextServiceBase {
}

Our starting point in this class is to define the Subject that our observable will use. As we noted, there are different Subject specializations in RxJS. As we only want our Subject to notify other classes of the latest value, we are going to use BehaviorSubject and set the current value to null:

private source = new BehaviorSubject(null);

We aren't going to expose the Subject to external classes; instead, we are going to create a new observable with this subject as the source. We do this so that, if we wanted to, we could customize the subscription logic—the throttling issue being an example of why we might want to do this:

context: this.source.asObservable();
We call this property the context property because it will carry the context of the change.

With this in place, external classes now have access to the observable source, so whenever we notify them that they need to react, they can. As the operation that we want to perform is based either on the user adding IPictureModel, or the data loading adding one, we are going to call the method that triggers the observable add chain. Our add method will receive the instance of the model that we want to send to our subscribing code:

public add(image: IPictureModel) : void {
this.source.next(image);
}

We identified that we needed two services to handle the different ways IPictureModel was received. The first service is called AddImageService and, as we would expect, can be generated for us by using Angular:

ng generate service services/AddImage

As we have already written the logic for our observable, our service simply looks like this:

export class AddImageService extends ContextServiceBase {
}

Our second service is called LoadImageService:

ng generate service services/LoadImage

Again, this class is going to extend ContextServiceBase:

export class LoadImageService extends ContextServiceBase {
}
At this point, you might be wondering why we have two services that appear to do the same thing. Theoretically, we could have had both do exactly the same thing. The reason that I chose to implement two versions comes back to understanding that one of the things that we want to do is display the image and trigger a save whenever we raise a notification through AddImageService. Suppose we also used AddImageService when the page was loaded. If we did this, then whenever the page was loaded, it would also trigger the save so we would end up duplicating images. Now, we could introduce filtering to prevent duplicates from happening, but I chose to keep things simple by using two separate classes for this first visit to RxJS. In the coming chapters, we will see how to make more complex subscriptions.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.150.59