Observables for the basic web app

Steven Guitar
ngconf
Published in
9 min readMay 2, 2019

Maybe you are like me.

I found that as I started my Angular journey, I had to think of observables as working out or getting exercise. It was not completely required, but it does have it’s benefits. It also requires utilization to gain proficiency — dabbling will not really get it to stick. This was definitely true for me. The more I tried to exercise the skills, the better I understood the library. With that said, there were some foundational aspects that I did not grasp in the beginning.

I decided to create a series which tries to talk about the high level list of topics around Observables on which I struggled without lots of exercise. For now, we will talk about where RxJS provides utility, and some level of a quick primer of how we got to this library being heavily integrated in Angular is in order. I also think that a lot of people are building sort of more boilerplate type of application which do not need the extended functionality provided by RxJS. However, gaining the understanding of RxJS can allow us to think about new ways our applications can function.

For a while, I had been a full stack developer with a more traditional skill set (Spring/Java, JSPs, HTML, CSS, jQuery, etc). Just like most folks, I had been building ajax based applications that were responsive and dynamic. Everything seemed obvious. Aside from laughing my way through a few “callback hell” scenarios, I had no real complaints because that was how the world works — right?

We had a team of our developers starting to build an application with Ember. When they would present their progress, inevitably this would include some amount of discussion around these things called Promises. In my typical curmudgeonly tech-dinosaur way, I would be happy to play the devils advocate role of “why”. While I am not really that ancient, I am a bit hesitant to completely invest into new frameworks or patterns until I can really see how it all is helpful. In development, there are so many smart people and new approaches to old problems that you can really cause problems by moving too quickly and creating a codebase that has no direction or consistency. Anyway… I really was slow to adopt the whole “promises thing”, until I started writing a backend for a side project in NodeJS. Before too long, I had really found the magic about promises and was completely able to embrace and deploy them with my development tasks.

As time went on, I started working on a fairly large AngularJS application. This is really only important to this post in that eventually we started planning a transition to Angular 4. We could see the roadmap and lifetime of AngularJS fading, and knew it was time. I had been learning about Angular and trying to use it for a few side projects to get prepared for the move. I was aware of the fact that its HttpClient was observable based rather than promise based, but to me, I saw ‘subscribe’ as simply another way to do a promise based then-able. I was wrong — but this is what I saw in my fast paced life of a small company with limited resources to do everything needed to build and maintain a SaaS application and company. In my adventure to learn about Angular, and by extension the sufficient amount of knowledge of Observables to build out an Angular prototype, I read articles and watched presentations to advance my understanding. What I saw over and over was basically the same few types of examples. Observables provide a way to respond to user actions, like mouse clicks, or changes to a source model. This makes sense.

As mentioned, observables leverage subscriptions. The concept of a subscription, also allows the ability to listen for an event, over a period of time. It works a lot like a subscription to a magazine. Every time an event occurs, we can respond to it, indefinitely, over time as long as the subscription is still active. In the example of a series of mouse clicks, or the changes to client model data, this makes a lot of sense. This is also the key differentiator from a promise. Promises basically die once you respond to the event.

When thinking about a typical web application and HTTP calls, the subscription concept holds up. We want to do something when data arrives. The only difference from promises, or even jQuery, is the ability to respond to the data coming in more than once for a particular request. This could be utilized in a real time dashboard, but for me and the typical applications I build, this is over kill. I just need to make a call, and display the data. In this sense, this feels very much like a promise.

This is the tip of the iceberg.

Observables in Angular are powered by RxJS, which provides something called “reactive extensions” to Javascript. This was completely new to me, which increased the stuff to learn while ramping up on Angular. You can try to side step it at first, but it really can be pretty powerful. It is used to create and manage subscriptions, as well as providing the ability to transform the data being observed.

Another key point in reactive programming that observables provide is the ability to treat data or events (or anything really) as the same type of thing. Consider the following:

var myButton = document.getElementById("myBtn");myButton.addEventListener("click", function(){
document.getElementById("demo").innerHTML = "Hello World";
});

The above snippet shows a simple and common way to listen for a button click in plain javascript, and do something when it happens.

Now, for something completely different, let’s consider the following:

var data = "Hello World";document.getElementById("demo").innerHTML = data;

In this example, we’re setting the same section on the HTML page with the same value. In the first case, it happens when the button is clicked, but in the second, we’re using client data to populate it.

The only difference between these two snippets really is that in the button scenario, the text will be set or reset for each button click. In the case of the data, it will be only set 1 time. Notice that in each case, the method of updating the UI is the same. So, we could combine these 2 things into one example, with a small bit of change:

var data = "Good Morning";
updateScreen(data);
var myButton = document.getElementById("myBtn");
myButton.addEventListener("click", function(){
updateScreen("Hello World");
});
function updateScreen(text) {
document.getElementById("demo").innerHTML = text;
}

Cool. Now we have a universally applicable function that is able to update the UI in a consistent fashion. What is also cool, is that it obviously does not care about the data or where it comes from.

Thinking about observables and subscribing to them so that we can respond to events — that seems to fit the click of our button.

var data = "Good Morning";
updateScreen(data);
var myButton = document.getElementById("myBtn");
Rx.Observable.fromEvent(myButton, 'click').subscribe(() => {
updateScreen("Hello World");
});
function updateScreen(text) {
document.getElementById("demo").innerHTML = text;
}

We are still short of having the perfect solution. If the data model were to be dynamically adjusted (which is common in front end applications these days), we do not have a way to update the value displayed. It would be really great if we could have a “change listener” on the data model as well, just like the button listener, to update the screen. How could we “observe” the model, and “subscribe” to know when it changes?

var data = "Good Morning";
Rx.Observable.from(data).subscribe((data) => {
updateScreen(data);
});
var myButton = document.getElementById("myBtn");
Rx.Observable.fromEvent(myButton, 'click').subscribe(() => {
updateScreen("Hello World");
});
function updateScreen(text) {
document.getElementById("demo").innerHTML = text;
}

Wow! So we have been able to solve the “change listener” for the data model. Additionally, look how similar this is for a button OR the data model. Aside from the difference between from() and fromEvent(), it is the same thing. We create an observable, and we subscribe to it. We could even have more than one of these subscriptions, and do common behavior on them.

var data = "Good Morning";
var myButton = document.getElementById("myBtn");
var sub1 = RxObservable.from(data);
var sub2 = RxObservable.fromEvent(myButton, 'click');
sub1.subscribe(updateScreen(data));
sub2.subscribe(updateScreen("Hello World"));
function updateScreen(text) {
document.getElementById("demo").innerHTML = text;
}

We have essentially transformed two completely different things, a data variable and a DOM button, to be handled in a universal way! This idea of pure functions that are easy to test and super reusable is at the core of reactive programming and RxJS.

The other interesting thing is the ability to transform the data in a very declarative way. There are a lot of ways this can manifest itself, but consider the following simple illustration.

var data = [
{id: 1, name: 'Steven Guitar'},
{id: 2, name: 'John Smith'}
];
var match;data.forEach((entry) => {
if (entry.name === 'Steven Guitar') {
match = entry;
}
});

This snippet is a fairly standard way of iterating the array of objects, trying to find one item by a particular property in the object. However, ES6 provides a more declarative way to do this.

var data = [
{id: 1, name: 'Steven Guitar'},
{id: 2, name: 'John Smith'}
];
var match = data.find(entry => entry.name === 'Steven Guitar');

This is fairly contrite, but can get more interesting when you use other functions like map, filter, and reduce. As an example, check out this snippet which transforms the data to have a separate first and last name, then finds all entries with first name of Steven, then lastly creating a new object with a nested array of the matches.

var data = [
{id: 1, name: 'Steven Guitar'},
{id: 2, name: 'John Smith'}
];
data.map(d => {
var nameParts = d.name.split(' ');
d.firstName = nameParts[0];
d.lastName = nameParts[1];
return d;
})
.filter(d => d.firstName === 'Steven')
.reduce((acc, curr) => {
if (!acc.matches) acc.matches = [];
acc.matches.push(curr);
return acc;
}, {});

map, filter, and reduce return new structures without modifying the originals. This allows them to be chained together in a very declarative way.

Just like javascript provides these functions to go through this transformation, RxJS provides the same functions called operators. There is even a map, filter opterator, which work the same way, yet are performed on a stream. Streams, are simply the series of events than observable produces. This is important to RxJS because it really allows us to not only subscribe and respond to things over time, but we can really transform the data we’re operating on.

Just as with pure javascript where you can chain these functions together, their counter part operations in RxJS provide a similar process. In effort to make the RxJS library a list of pure functions and allow us to add our own custom operators, this concept of “lettable” operators was created. This means that instead of chaining the operators together, which requires that these functions are functions directly on the Observable, instead we can string them together in a “pipe”. This looks like the following:

Rx.Observable.fromEvent(domElement, 'click').pipe(
map(),
filter(),
reduce()
).subscribe(() => {
// do something with the transformed, filtered, and modified result
});

Essentially this means that we will transform the click event, only keep certain click events, and then create some new observable data structure based on the filtered and transformed source data. Rather than call each map, filter, and reduce directly on observable, we can just execute pure functions and chain them together in any way we need to. We can create our own pure functions as an observable operation!

So, if you are like me, you are working on a fairly standard web based application. As an example, my first real venture into Angular was to prototype an AngularJS project so that we could evaluate our migration efforts. The application is fairly standard. It makes HTTP calls asynchronously, and populates data on the screen when it arrives. Button clicks were primarily handled with the ‘ng-click’ binding on the html element in the template. It does not do any polling, nor is the data currently ‘real-time’ beyond being fresh from the moment the HTTP call was made. To me, in a post jQuery world, this is very standard and common, or even basic for applications being built on the web. It may not be super modern, or rich with cool features, but it is enough to get a decent client experience on the web.

For most of my applications, I am also the backend developer. If there is a discrepancy with a data structure on the client side or the server side, as long as I consider changes to the contract as needed, I really can control either side of that without much fuss. There is not much need for client side data manipulation if you can get it organized as needed from the server.

As I built out this kind of application, it is easy to basically swap out Promise().then() for Observable().subscribe() and ignore everything else. However, as you add more services to the application, or want to “react” to UI state or DOM events more robustly, diving in to the RxJS world is inevitable. As I got more into it, I found the same “mouse click” examples, and “interval/timeout” examples to be so contrite, it caused me confusion on how to apply these operators to the real world. I wanted to share the observations I have had in learning RxJS within my development on Angular applications.

Conclusion

This article is the first in a series which will dive more into using Observables with Angular development. Amongst other things that will naturally pop up, in particular, I will discuss

  • Managing Subscriptions and their lifetime
  • Transforming HTTP data
  • Exploring some common operators

This article is setting the ground work with the concepts of reactive programming, and what problems that RxJS aims to assist with. We discussed some common use cases for observables that any application can take advantage of.

EnterpriseNG is coming

EnterpriseNG is a two-day conference from the ng-conf folks coming on November 19th and 20th. Check it out at ng-conf.org

--

--

Steven Guitar
ngconf
Writer for

Self employed, full stack developer currently working with Angular and NodeJS running microservices on AWS with efficient DevOps practices