Angular Observables: How to Use Observables In Angular

Angular Observables provide the support for passing the messages between publishers(Creator of Observables) and subscribers(User of Observables) in your application. Observables are declarative that is, you define the function for publishing values, but it is not executed until the consumer subscribes to it. We have already covered the Angular 9 Tutorial on this blog.

The observable can deliver the multiple values of any type like literals, messages, or events, depending on the context. As a publisher, you can create an Observable instance that defines a subscriber function. This is a function that is executed when the consumer calls the subscribe() method.

Define Angular Observers

The handler for receiving the observable notifications implements the Observer interface. It is an object that defines the callback methods to handle the three types of notifications that an observable can send. These are the following.

next: Required. The handler for each delivered value called zero or more times after execution starts. error: Optional. The handler for error notification. The error halts the execution of the observable instance. complete: Optional. The handler for an execution-complete notification. The delayed values can continue to be delivered to a next handler after execution is complete.

Subscribing Angular Observables

An important note here is that the Observables instance begins publishing the values only when someone subscribes to it. You can subscribe to the observables by calling the subscribe() method of the instance and passing an observer object to receiving the notifications.

myObservable.subscribe( x => console.log('Observer got a next value: ' + x), err => console.error('Observer got an error: ' + err), () => console.log('Observer got a complete notification') );

If we need to show how subscribing works, we need to create a new observable. There is a constructor that you use to create the new instances, but for the illustration, we can use the methods from the RxJS library that create the simple observables of frequently used types:

of(…items)

Returns an Observable instance that synchronously delivers the values provided as arguments.

from(iterable)

Converts its argument to an Observable instance. The method is commonly used to convert the array to an observable.

Creating Angular Observables

Use the Observables constructor to create an observable stream of any type. We will develop a Stream of type Array in our example. The constructor takes argument the subscriber function to run when an observable’s subscribe() method executes.

The subscriber function receives an Observer object and can publish the values to the observer’s next() method.

You can create a new observable like this.

const obs = new Observable();

You will learn how we can use Observables in Real life by below Angular Observables Example.

Angular 9 Observables Example

We start Angular Observables by learning an example. So let us install the Angular 9 Project using Angular CLI.

Step 1: Create an Angular 9 Project

Type the following command to create an Angular project using Angular CLI.

ng new observe

Now, go inside the project and open the project in VSCode or any other editor.

cd observe && code .

Also, install the Bootstrap CSS Framework.

npm install bootstrap --save

Step 2: Create a service and model files

Create an Angular service.

ng g s student --spec=false

It will create a file student.service.ts file inside the src >> app folder.

We have created a service because we will use the service to handle the data that needs to be displayed on the frontend.

Also, create a new file inside an src >> app directory called student.model.ts and add the following code inside it.

// student.model.ts export class Student { id: Number; name: String; EnrollmentNumber: Number; College: String; University: String; }

That means we have defined the Student type in our application which has the id, name, enrollment number, college, and university properties.

Now, we need to add the demo data inside the student.service.ts file. The data is the type of Student model which we have defined above.

// student.service.ts import { Injectable } from '@angular/core'; import { Student } from './student.model'; @Injectable({ providedIn: 'root' }) export class StudentService { students: Student[] = [{ id: 1, name: 'Krunal', enrollmentnumber: 110470116021, college: 'VVP Engineering College', university: 'GTU' }, { id: 2, name: 'Rushabh', enrollmentnumber: 110470116023, college: 'VVP Engineering College', university: 'GTU' }, { id: 3, name: 'Ankit', enrollmentnumber: 110470116022, college: 'VVP Engineering College', university: 'GTU' }]; constructor() { } }

Step 3: Create an Observable

Now, we have defined the private data. We need to create one function inside the service that will return that data in the form of observable. So we can subscribe to it, and we get the data and display it on the frontend. Add the following code inside the student.service.ts. Put the getStudents() function inside the class after the constructor.

// student.service.ts import { Observable } from 'rxjs'; public getStudents(): any { const studentsObservable = new Observable(observer => { setTimeout(() => { observer.next(this.students); }, 1000); }); return studentsObservable; }

So, here we have done is first import the Observable from rxjs. It has then defined one function that will return an observable. The observable object gets one argument that has a timeout function. So after 1 second, it will produce the whole student’s array if the subscriber subscribes the observable.

In simple terms, here studentObservable are publishing our primary data array that is students. So if any entity needs to get the values out of observable, then it first needs to subscribe that observable and then studentObservable starts to publish the values, and then subscriber get the values.

Step 4: Define the Subscriber

We have created the Publisher for the Observables. Now, we need to create a subscriber. So write the following code inside the app.component.ts file.

// app.component.ts import { Component, OnInit } from '@angular/core'; import { Student } from './student.model'; import { StudentService } from './student.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { students: Student[] = []; constructor(private studentservice: StudentService) {} ngOnInit() { const studentsObservable = this.studentservice.getStudents(); studentsObservable.subscribe((studentsData: Student[]) => { this.students = studentsData; }); } }

Here, we have subscribed the observable and get the students data.

The final step is to display the data.

Step 5: Display the data

Add the following code inside the app.component.html file.

<!-- app.component.html> <div class="container"> <div class="row" style="margin-top: 30px"> <div class="col-md-3 col-xs-6" *ngFor="let student of students"> <div class="card"> <div class="card-body"> <h5 class="card-title">{{ student.name }}</h5> <h6 class="card-subtitle">{{ student.enrollmentnumber }}</h6> <p class="card-text">{{ student.college }}</p> <p class="card-text">{{ student.university }}</p> <a class="btn btn-primary" href="#" >Go somewhere</a> </div> </div> </div> </div> </div>

Save the file and start the angular development server.

ng serve

Go to the http://localhost:4200 and see the result after 1 second.

So, we have successfully got the data using Observables.

Remember, Observables producing the values Asynchronously. Observables are the lazy collections of multiple values or streams over time. It is like a subscription to the newsletter, if you keep that subscription open, you will get the new one every once and a while.

The sender decides when you get it, but all you have to do is to wait until it comes straight into your inbox.

The critical thing here is to understand when using observables is that observables push. Push and pull are two different ways that describe how the data producer communicates with the data consumer.

Simple Code of Observables

import { Observable } from "rxjs/Observable" // create observable const simpleObservable = new Observable((observer) => { // observable execution observer.next("hello") observer.complete() }) // subscribe to the observable simpleObservable.subscribe() // dispose the observable simpleObservable.unsubscribe()

Multicasting in Angular

A regular observable creates the new, independent execution for each subscribed observer.

When the observer subscribes, the observable wires up the event handler and gives values to that observer.

When the second observer subscribes, the observable then wire up a new event handler and gives values to that second observer in the separate execution.

Sometimes, instead of starting the independent execution for each subscriber, you require each subscription to get the same values even if values have already started emitting.

It might be the case with something like the observable of clicks on the document object.

Multicasting is a practice of broadcasting to a list of multiple subscribers in a single execution.

With multicasting observable, you don’t register multiple listeners on the document, but instead, re-use the first listener and send values out to each subscriber.

When creating the observable you should resolve how you want that observable to be used and whether or not you want to multicast its values.

Let’s look at an example that counts from 1 to 3, with a one-second delay after each number emitted.

function sequenceSubscriber(observer) { const seq = [1, 2, 3]; let timeoutId; // Will run through an array of numbers, emitting one value // per second until it gets to the end of the array. function doSequence(arr, idx) { timeoutId = setTimeout(() => { observer.next(arr[idx]); if (idx === arr.length - 1) { observer.complete(); } else { doSequence(arr, ++idx); } }, 1000); } doSequence(seq, 0); // Unsubscribe should clear the timeout to stop execution return {unsubscribe() { clearTimeout(timeoutId); }}; } // Create a new Observable that will deliver the above sequence const sequence = new Observable(sequenceSubscriber); sequence.subscribe({ next(num) { console.log(num); }, complete() { console.log('Finished sequence'); } }); // Logs: // (at 1 second): 1 // (at 2 seconds): 2 // (at 3 seconds): 3 // (at 3 seconds): Finished sequence

One thing to note that if you subscribe twice, there will be two separate streams, each emitting values every second. It looks something like this:

// Subscribe starts the clock, and will emit after 1 second sequence.subscribe({ next(num) { console.log('1st subscribe: ' + num); }, complete() { console.log('1st sequence finished.'); } }); // After 1/2 second, subscribe again. setTimeout(() => { sequence.subscribe({ next(num) { console.log('2nd subscribe: ' + num); }, complete() { console.log('2nd sequence finished.'); } }); }, 500); // Logs: // (at 1 second): 1st subscribe: 1 // (at 1.5 seconds): 2nd subscribe: 1 // (at 2 seconds): 1st subscribe: 2 // (at 2.5 seconds): 2nd subscribe: 2 // (at 3 seconds): 1st subscribe: 3 // (at 3 seconds): 1st sequence finished // (at 3.5 seconds): 2nd subscribe: 3 // (at 3.5 seconds): 2nd sequence finished

Changing the observable to be a multicasting could look something like this:

function multicastSequenceSubscriber() { const seq = [1, 2, 3]; // Keep track of each observer (one for every active subscription) const observers = []; // Still a single timeoutId because there will only ever be one // set of values being generated, multicasted to each subscriber let timeoutId; // Return the subscriber function (runs when subscribe() // function is invoked) return (observer) => { observers.push(observer); // When this is the first subscription, start the sequence if (observers.length === 1) { timeoutId = doSequence({ next(val) { // Iterate through observers and notify all subscriptions observers.forEach(obs => obs.next(val)); }, complete() { // Notify all complete callbacks observers.slice(0).forEach(obs => obs.complete()); } }, seq, 0); } return { unsubscribe() { // Remove from the observers array so it's no longer notified observers.splice(observers.indexOf(observer), 1); // If there's no more listeners, do cleanup if (observers.length === 0) { clearTimeout(timeoutId); } } }; }; } // Run through an array of numbers, emitting one value // per second until it gets to the end of the array. function doSequence(observer, arr, idx) { return setTimeout(() => { observer.next(arr[idx]); if (idx === arr.length - 1) { observer.complete(); } else { doSequence(observer, arr, ++idx); } }, 1000); } // Create a new Observable that will deliver the above sequence const multicastSequence = new Observable(multicastSequenceSubscriber()); // Subscribe starts the clock, and begins to emit after 1 second multicastSequence.subscribe({ next(num) { console.log('1st subscribe: ' + num); }, complete() { console.log('1st sequence finished.'); } }); // After 1 1/2 seconds, subscribe again (should "miss" the first value). setTimeout(() => { multicastSequence.subscribe({ next(num) { console.log('2nd subscribe: ' + num); }, complete() { console.log('2nd sequence finished.'); } }); }, 1500); // Logs: // (at 1 second): 1st subscribe: 1 // (at 2 seconds): 1st subscribe: 2 // (at 2 seconds): 2nd subscribe: 2 // (at 3 seconds): 1st subscribe: 3 // (at 3 seconds): 1st sequence finished // (at 3 seconds): 2nd subscribe: 3 // (at 3 seconds): 2nd sequence finished

Multicasting observables take a bit more setup, but they can be useful for certain applications.

Later we will look at tools that simplify the process of multicasting, allowing you to take any observable and make it multicasting.

Error handling

The Observables produce values asynchronously; that is why try/catch block will not effectively work here to catch the errors. So In the sense of Observables, you can handle the errors by specifying an error callback on the observer.

Producing an error also causes the observable to clean up subscriptions and stop producing values. An observable can either produce the values (calling the next callback), or it can complete, calling either the complete or error callback.

myObservable.subscribe({ next(num) { console.log('Next num: ' + num)}, error(err) { console.log('Received an errror: ' + err)} });

Disposing observables

When you subscribe to the Observable, you get back the subscription, which represents an ongoing execution. Just call unsubscribe() to cancel the execution. So you are free to that Observable.

You can find more about Angular Observables here.

Finally, Angular 9 Observables Example is over.

Recommended Posts

Angular 9 Forms Example

Angular 9 File Upload Example

Angular 9 HttpClient Example

Angular 9 CRUD Example