November 27, 2019

Getting Started with Observables in Angular

by Ajdin Imsirovic

jscrambler-blog-getting-started-observables-angular

Many operations in JavaScript and on the web can result in data streams. A data stream is just a sequence of values that arrives over time.

An observable is like a function that returns a data stream, either synchronously (at once), or asynchronously (over time).

The difference between an observable and an actual function is the following:

  • With functions, we control when we get the value out (we pull the value out by calling the function — i.e making the code inside the function run and return a value);
  • With observables, we only listen to the Observable, which pushes the values to us; thus, the Observable is in control.

Where does this data come from?

It can come from a variety of sources, for example, our users interacting with the page through mouse events and key events. They can also come in the form of responses from third-party API HTTP calls. We can also emit data structures into observable streams ourselves. These data structures can be any kind of data, for example, arrays or objects. Sometimes, the data source is referred to as “the Producer”, because it supplies the data stream to the Observable.

This "data over time" concept is great when working with asynchronous, event-based applications.

Here's a real-world metaphor that we can use to get our head around the concept of observables.

Let's say you want to watch a TV show from any of the available online show-streaming services.

What you'll do to watch a TV show online can be summed up in a few steps:

  1. Visit the streaming service and locate the show to watch;
  2. Click the play button;
  3. The show starts to arrive over time, episode by episode;
  4. You want to keep binge-watching but you must go to work — so you click the stop button.

Note: in this example, you can keep watching if there are episodes available. When there are no episodes available, you will keep listening for new episodes whenever they appear — unless the streaming service has marked the series as over, as we’ll explain below.

In the above scenario:

  • the streaming show is the Observable stream;
  • the person watching the show is the Observer;
  • the act of pressing the play button is known as subscribing;
  • the act of pressing the stop button is called unsubscribing;

You are the Observer and the show that's streaming is the Observable. Being the Observer, you can get three different notifications: next, error, and complete.

Whenever the next episode comes through the data stream, you get the next notification.

If there was an error, you'll get an error notification.

Once the entire show stream is complete, you get the complete notification — so you know there are no more episodes to watch and the player stops on its own.

What is RxJS?

RxJS is a library that makes it possible for us to work with these streams of data in JavaScript.

In RxJS, the Observer is an Object with 3 built-in methods: next(), error(), and complete(). In RxJS parlance, we say that each episode is emitted from the stream. Thus, whenever a new episode is emitted, the next() method gets called.

If we need to do something each time an episode is emitted, we pass that as a parameter to the next() method.

For example:

next( eatAPieceOfPizza() )

Great, whenever a new episode is emitted, besides watching the stream, we're also having a piece of pizza. This is known as handling the next condition.

If an error occurs, such as our internet connection breaking, we can handle the error condition like this:

error( takeAWalk() )

When the stream is complete, we can read a book, like this:

complete( readABook() )

An Observer is like a person that watches the data stream and — based on what emits from the stream (i.e based on notifications from the stream) — does some predefined action accordingly.

Thus, the Observable delivers (emits) some values, and the Observer knows what to do with them.

What kind of data can be emitted from an Observable stream?

An Observable stream can emit virtually any kind of data:

  • primitive values such as strings or numbers;
  • click events (or any other event for that matter!);
  • responses from HTTP requests, etc.

Next, we'll see how we can convert a series of mousemove events into an Observable in Angular 8.

Subscribing to an Observable of mousemove events in Angular 8

For this, we'll use a simple app hosted on Stackblitz.

We'll first need to build an Observable.

To build it, we'll use the fromEvent method, which we'll import like this:

import { fromEvent } from 'rxjs/observable/fromEvent';

Now that we can build Observable streams with the fromEvent method, let's actually build one.

We'll make an Observable from mousemove events in the window object:

observable = fromEvent(window, 'mousemove');

However, even though the data is there, we can't receive it until we subscribe to the Observable stream.

That's why we'll add a subscription right inside our component's constructor:

  constructor(){

    this.observable.subscribe(
      theNextItem => console.log('You moved your mouse'),
      anErrorHappened => console.error('There was an error'),
      () => console.log('the Observable stream has ended')
    );
    
  }

Protect your Angular App with Jscrambler

We're taking care of each of the three built-in scenarios: next(), error(), and complete().

Note that these three are passed in as ES6 anonymous functions, with function parameters given some custom names.

We could have written any other parameter name, such as:

  constructor(){

    this.observable.subscribe(
      x => console.log('You moved your mouse'),
      x => console.error('There was an error'),
      () => console.log('the Observable stream has ended')
    );
    
  }

Note that we can also optionally list only the handler for our Observer's next() method, like this:

  constructor(){
    this.observable.subscribe( x => console.log('You moved your mouse') );
  }

We don't have to pass a handler for all the Observer's methods — in fact, we're only taking care of Observer.next() in the code above.

If we choose not to pass a handler for either one of these notifications, the Observer will simply ignore them. However, the first one we pass in will always be the next() method, the second one will be the error(), and the third one will be the complete() method.

Next, let's see an example of handling an HTTP request with an Observable in Angular 8.

Handling an HTTP request with an Observable in Angular 8

To make HTTP requests in Angular, we use HttpClient, so let's import it:

import { HttpClient } from '@angular/common/http';

Next, let's inject it into the constructor:

constructor(private http = HttpClient) {

For our data source, we'll use Twitter's API. Feel free to see more info in the official documentation.

Now we'll build the Observable using HttpClient's get method:

this.http.get('https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=twitterapi&count=5')

We'll also need to wrap the call to the above API inside the NgOnInit lifecycle hook:

ngOnInit() {
  this.http.get('https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=twitterapi&count=5')  
}

We'll also need to add the necessary imports into app.module.ts. Here's the whole updated file:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'; 
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';

@NgModule({
  imports:      [ BrowserModule, FormsModule, HttpModule, HttpClientModule ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ],
  providers: [ HttpClientModule ]
})
export class AppModule { }

Finally, we'll need to pass in an anonymous function to handle the error() method on our Observer object that we pass to the subscribe method:

import { Component, OnInit } from '@angular/core';
import { fromEvent } from 'rxjs/observable/fromEvent';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html'
})
export class AppComponent  {

  
  constructor(private http: HttpClient){}

  ngOnInit() {
      this.http.get('https://developer.twitter.com/en/docs/tweets/timelines/api-reference/get-statuses-user_timeline').subscribe( 
        x => console.log(x),
        x => console.error('%c no data came in', 'font-size: 20px; color: white; background: tomato'  )
      );    
  }

}

Of course, since Twitter's API requires authentication, we'll only see "no data came in" inside the console.

Finally, to test what running the Observer.next() method will be like, let's connect to a different, open-source API. You can easily find free APIs in this GitHub list.

In this example, we'll be getting the Bitcoin price index from the coindesk.com database, so let's update our http.get() call to this:

this.http.get('https://api.coindesk.com/v1/bpi/currentprice.json')

What we get back inside our DevTools console is some data on the current Bitcoin price.

And… that sums up our second Observables example in Angular 8! Hopefully, both examples have illustrated the possibilities of using Observables.

Feel free to test out other sources of API data, or build your own and serve it to your Angular app.

Finally, don't forget to pay special attention if you're developing commercial Angular apps that contain sensitive logic. You can protect them against code theft, tampering, and reverse engineering by following our guide.