Web Development

Getting Started with Observables in Angular

November 27th, 2019 | By Ajdin Imsirovic | 6 min read

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

An observable is like a function that returns a data stream 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 it 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 many sources. Data can come from the user's interactions with the page through mouse events and key events, for example.

They can also come as responses from third-party API HTTP calls. We can also emit data structures into observable streams ourselves. These data structures can be any 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: We can use this to get our heads around the concept of observables.

Let's say you want to watch a TV show on an online streaming service.

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. However, 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 you don't have episodes available, you will keep listening for new episodes whenever they are released, unless the streaming service has marked the series as over, as we 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 observer. 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 is an error, you'll get an error notification.

Once the entire show stream is complete, you get a 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 three 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 who watches the data stream and, based on what it emits (i.e., notifications from the stream), takes 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 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:

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


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 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 information 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 on 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.

Conclusion

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.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Web Development

Vuex vs Global Store(Vue.Observable)

In this article, we will learn what Vuex and Vue.observable are, their differences, and which one is ideal for managing your store application.

January 11, 2023 | By Jscrambler | 3 min read

Web Development

Building an app with Angular & Firebase

In this tutorial, you will learn how to build a basic CRUD app using Angular and Firebase

August 26, 2021 | By Jay Raj | 11 min read

Section Divider