Angular Observable Tutorial
Angular Observable Tutorial
Angular Observable Tutorial
The Angular Observable tutorial covers what is observable and how to use Observables
in Angular applications. When we talk about Angular Observable, we hear a lot of terms
like Reactive programming, data streams, Observable, Observers, RxJS, etc. It is essential
to understand these terms before we start using the observables.
Rx stands from Reactive programming. It is defined as programming with
asynchronous data streams. So it is important that you understand what is data stream is.
Table of Contents
What is a data stream
Reactive Programming
What is RxJS
Observable in Angular
What is an Observable in Angular
Who are observers (subscribers)
Angular Observable tutorial
o Import the required libraries
o Observable Creation
o Subscribing to the observable
o Adding interval
o Error event
o Complete Event
Observable Operators
Unsubscribing from an Observable
References
Summary
What is a data stream
A stream is data that arrives over some time. The stream of data can be anything. Like
variables, user inputs, properties, caches, data structures, and even failures, etc
Consider the example of a sequence of x & y positions of mouse click events. Assume that
the user has clicked on the locations (12,15), (10,12), (15,20) & (17,15) in that order.
The following diagram shows how the values arrive over a period of time. As you can see,
the stream emits the values as they happen, i.e., asynchronously.
1
mouse click events as data streams
Value is not the only thing that stream emits. The stream may complete as the user closes
the window or app. Or an error may happen, resulting in the stream’s closure. At any point
in time stream may emit the following three things.
Value: i.e., the next value in the stream
Complete: The stream has ended
Error: The error has stopped the stream.
The following diagram shows all the three possibilities in a stream
m
ouse click events as data streams with emit error and complete events
As said earlier the stream of data can be anything. For Example
Mouse click or Mouse hover events with x & y positions
Keyboard events like keyup, keydown, keypress, etc
Form events like value changes etc
Data which arrives after an HTTP request
User Notifications
Measurements from any sensor
2
Important Points regarding streams can
Emit zero, one or more values of any time.
Can also emit errors.
Must emit the complete signal, when completes (finite streams).
Can be infinite, that they never complete
Now we have understood what is a data stream, let us look at what is Reactive
Programming is
Reactive Programming
Reactive programming is about creating the stream, emitting value, error, or complete
signals, manipulating, transferring, or doing something useful with the data streams.
This is where the RxJs comes into the picture
The introduction to Reactive Programming you’ve been missing gives you a very nice
introduction to Reactive Programming. Also, refer to Introduction to Rx
What is RxJS
The RxJS (Reactive Extensions Library for JavaScript) is a Javascript library that allows us
to work with asynchronous data streams.
Observable in Angular
Angular uses the RxJS library heavily in its framework to implement Reactive Programming.
Some of the examples where reactive programming is used are
Reacting to an HTTP request in Angular
Value changes / Status Changes in Angular Forms
The Router and Forms modules use observables to listen for and respond to user-
input events.
You can define custom events that send observable output data from a child to a
parent component.
The HTTP module uses observables to handle AJAX requests and responses.
The RxJs has two main players
1. Observable
2. Observers ( Subscribers)
What is an Observable in Angular
Observable is a function that converts the ordinary stream of data into an observable
stream of data. You can think of Observable as a wrapper around the ordinary stream of
data.
An observable stream or simple Observable emits the value from the
stream asynchronously. It emits the complete signals when the stream completes or
an error signal if the stream errors out.
Observables are declarative. You define an observable function just like any other variable.
The observable starts to emit values only when someone subscribes to it.
Who are observers (subscribers)
The Observable on its own is only useful if someone consumes the value emitted by the
observable. We call them observers or subscribers.
The observers communicate with the Observable using callbacks
The observer must subscribe with the observable to receive the value from the observable.
While subscribing it optionally passes the three callbacks. next(), error() & complete()
3
Angular Observable Tutorial how
observable and observers communicates with callbacks
The observable starts emitting the value as soon as the observer or consumer subscribes
to it.
The observable invokes the next() callback whenever the value arrives in the stream. It
passes the value as the argument to the next callback. If the error occurs, then
the error() callback is invoked. It invokes the complete() callback when the stream
completes.
Observers/subscribers subscribe to Observables
The observer registers three callbacks with the observable at the time of subscribing.
i .e next(), error() & complete()
All three callbacks are optional
The observer receives the data from the observer via the next() callback
They also receive the errors and completion events from the Observable via
the error() & complete() callbacks
Angular Observable tutorial
Now, we have learned the basics of the RxJs Observable, let us now see how it works
using an example.
Create a new project in angular. Remove the contents from app.component.html. Open
the app.component.ts
Import the required libraries
RxJs library is installed automatically when you create the Angular project. Hence there is
no need to install it.
Import the Observable from the rxjs library
1
2 import { Observable } from 'rxjs';
3
Observable Creation
There are a few ways in which you can create observable in angular. The simplest is to use
the Observable constructor. The observable constructor takes the observer (or subscriber)
as its argument. The subscriber will run when this observable’s subscribe() method
executes.
The following example creates an observable of a stream of numbers 1, 2, 3, 4, 5
1
2 obs = new Observable((observer) => {
3 console.log("Observable starts")
4 observer.next("1")
4
5 observer.next("2")
6 observer.next("3")
7 observer.next("4")
8 observer.next("5")
9 })
10
Source Code
The variable obs is now of the type of observable.
The above example declares the obs as observable but does not instantiate it. To make the
observable emit values, we need to subscribe to them.
Creatin
g observable in Angular Observable Tutorial app
In the above example, we used the Observable Constructor to create the Observable. Many
operators are available with the RxJS library, which makes creating the observable easy.
These operators help us to create observables from an array, string, promise, any iterable,
etc. Here are list of some of the commonly used operators
create
defer
empty
from
fromEvent
interval
of
range
throwError
timer
Subscribing to the observable
We subscribe to the observable by invoking the subscribe method on it.
We either pass an observer object or the next() callback as an argument. The arguments
are optional.
The subscribe method signature was changed in RxJs 6.4. Scroll down for older syntax
An observer object is an object that optionally contains the
next, error and complete methods. The signature of the observer object is shown below.
1
2 export interface Observer<T> {
3 next: (value: T) => void;
4 error: (err: any) => void;
5
5 complete: () => void;
6}
7
The code below shows subscribing to an observable using the observer object.
The next method is invoked whenever the observable emits data. It invokes
the error method when an error occurs and the complete method when the observable
completes.
1
2 ngOnInit() {
3 this.obs.subscribe(
4 {
5 next: (val) => {
6 console.log(val);
7 }, //next callback
8 error: (error) => {
9 console.log('error');
10 }, //error callback
11 complete:() => {
12 console.log('Completed');
13 } //complete callback
14 }
15 );
16 }
17
The complete app.component.ts code is as shown below.
1
2 import { Component, OnInit } from '@angular/core';
3 import { Observable } from 'rxjs';
4
5 @Component({
6 selector: 'my-app',
7 template: `
8
9 <h1>Angular Observable Tutorial</h1>
10
11 <br><br><br>
12 Refer
13 <a href="https://www.tektutorialshub.com/angular/angular-observable-tutorial-using-
14 rxjs/">Angular Observable
15 Tutorial</a>
16 `
17 })
18 export class AppComponent implements OnInit {
19
20
21 obs = new Observable(observer => {
22 console.log('Observable starts');
6
observer.next('1');
23
observer.next('2');
24
observer.next('3');
25
observer.next('4');
26
observer.next('5');
27
});
28
29
ngOnInit() {
30
this.obs.subscribe( {
31
next: (val) => {
32
console.log(val);
33
}, //next callback
34
error: (error) => {
35
console.log('error');
36
}, //error callback
37
complete:() => {
38
console.log('Completed');
39
} //complete callback
40
}
41
);
42
}
43
}
44
45
7
8 styleUrls: ['./app.component.css']
9 })
10 export class AppComponent implements OnInit {
11 title = 'Angular Observable using RxJs - Getting Started';
12
13 obs = new Observable((observer) => {
14 console.log("Observable starts")
15 observer.next("1")
16 observer.next("2")
17 observer.next("3")
18 observer.next("4")
19 observer.next("5")
20 })
21
22 data=[];
23
24 ngOnInit() {
25
26 this.obs.subscribe(
27 val=> { console.log(val) },
28 error => { console.log("error")},
29 () => {console.log("Completed")}
30 )
31 }
32 }
33
Source Code
Now, run the code and watch the debug window.
Adding interval
We can add a timeout to insert a delay in each next() callback
1
2 obs = new Observable((observer) => {
3 console.log("Observable starts")
4
5 setTimeout(() => { observer.next("1") }, 1000);
6 setTimeout(() => { observer.next("2") }, 2000);
7 setTimeout(() => { observer.next("3") }, 3000);
8 setTimeout(() => { observer.next("4") }, 4000);
9 setTimeout(() => { observer.next("5") }, 5000);
10
11 })
12
Source Code
8
An
gular Observable tutorial example app
Error event
As mentioned earlier, the observable can also emit an error. This is done by invoking
the error() callback and passing the error object. The observables stop after emitting the
error signal. Hence values 4 & 5 are never emitted.
1
obs = new Observable((observer) => {
2
console.log("Observable starts")
3
4
setTimeout(() => { observer.next("1") }, 1000);
5
setTimeout(() => { observer.next("2") }, 2000);
6
setTimeout(() => { observer.next("3") }, 3000);
7
setTimeout(() => { observer.error("error emitted") }, 3500); //sending error event.
8
observable stops here
9
setTimeout(() => { observer.next("4") }, 4000); //this code is never called
10
setTimeout(() => { observer.next("5") }, 5000);
11
12
})
13
Source Code
You can send the error object as the argument to the error method
Observable
with the error event
9
Complete Event
Similarly the complete event. The observables stop after emitting the complete signal.
Hence values 4 & 5 are never emitted.
1
obs = new Observable((observer) => {
2
console.log("Observable starts")
3
4
setTimeout(() => { observer.next("1") }, 1000);
5
setTimeout(() => { observer.next("2") }, 2000);
6
setTimeout(() => { observer.next("3") }, 3000);
7
setTimeout(() => { observer.complete() }, 3500); //sending complete event. observable
8
stops here
9
setTimeout(() => { observer.next("4") }, 4000); //this code is never called
10
setTimeout(() => { observer.next("5") }, 5000);
11
12
})
13
Source Code
Observable
with complete event
Observable Operators
The Operators are functions that operate on an Observable and return a new Observable.
The power of observable comes from the operators. You can use them to manipulate the
incoming observable, filter it, merge it with another observable, alter the values or subscribe
to another observable.
You can also chain each operator one after the other using the pipe. Each operator in the
chain gets the observable from the previous operator. It modifies it and creates a new
observable, which becomes the input for the next observable.
The following example shows the filer & map operators chained inside a pipe. The filter
operator removes all data which is less than or equal to 2 and the map operator multiplies
the value by 2.
The input stream is [1,2,3,4,5] , while the output is [6, 8, 10].
1
2 obs.pipe(
3 obs = new Observable((observer) => {
4 observer.next(1)
5 observer.next(2)
6 observer.next(3)
7 observer.next(4)
10
8 observer.next(5)
9 observer.complete()
10 }).pipe(
11 filter(data => data > 2), //filter Operator
12 map((val) => {return val as number * 2}), //map operator
13 )
14
The following table lists some of the commonly used operators
AREA OPERATORS
Combination combineLatest, concat, merge, startWith , withLatestFrom, zip
Filtering debounceTime,
distinctUntilChanged, filter,
take, takeUntil, takeWhile, takeLast, first, last, single, skip, skipUntil, skipWhile, skip
Transformation bufferTime, concatMap, map, mergeMap, scan, switchMap, ExhaustMap, reduce
Utility tap, delay, delaywhen
Error Handling throwerror, catcherror, retry, retrywhen
Multicasting Share
Unsubscribing from an Observable
We need to unsubscribe to close the observable when we no longer require it. If not it may
lead to memory leak & Performance degradation.
To Unsubscribe from an observable, we need to call the Unsubscribe() method on the
subscription. It will clean up all listeners and frees up the memory.
To do that, first, create a variable to store the subscription
1
2 obs: Subscription;
3
Assign the subscription to the obs variable
1
2
3 this.obs = this.src.subscribe(value => {
4 console.log("Received " + this.id);
5 });
6
7
Call the unsubscribe() method in the ngOnDestroy method.
1
2 ngOnDestroy() {
3 this.obs.unsubscribe();
4}
5
When we destroy the component, the observable is unsubscribed and cleaned up.
But, you do not have to unsubscribe from every subscription. For Example, the
observables, which emits the complete signal, close the observable.
To learn more about it refer to the tutorial Unsubscribing from an Observable in Angular.
References
1. observables
2. RX-library
11
3. observables in angular
4. Practical observable usage
5. Comparing observables
6. Observable Design Pattern
Create observable from a string, array & object in angular
8 Comments / March 9, 2023 / 5 minutes of reading
Angular Observable Tutorial
Angular Tutorial
In this tutorial, we will show you how to create observable using create, of, from operators
in Angular. We can use them to create new observable from the array, string, object,
collection or any static data. Also learn the difference between the Of & From operators. If
you are new to observable, we recommend you to read the Angular observable before
continuing here.
Table of Contents
Observable creation functions
Create
o Observable Constructor
Of Operator
o observable from an array
o observable from a sequence of numbers
o observable from string
o observable from a value, array & string
From Operator
o observable from an array
o Observable from string
o Observable from collection
o Observable from iterable
o Observable from promise
Of Vs From
References
Summary
Observable creation functions
There are many ways to create observable in Angular. You can make use of Observable
Constructor as shown in the observable tutorial. There are a number of functions that are
available which you can use to create new observables. These operators help us to create
observable from an array, string, promise, any iterable, etc. Here are some of the operators
create
defer
empty
from
fromEvent
interval
of
range
throw
12
timer
All the creation related operators are part of the RxJs core library. You can import it from
the ‘rxjs’ library
Create
The Create method is one of the easiest. The create method calls the observable
constructor behind the scene. Create is a method of the observable object, Hence you do
not have to import it.
1
2 ngOnInit() {
3
4 //Observable from Create Method
5 const obsUsingCreate = Observable.create( observer => {
6 observer.next( '1' )
7 observer.next( '2' )
8 observer.next( '3' )
9
10 observer.complete()
11 })
12
13 obsUsingCreate
14 .subscribe(val => console.log(val),
15 error=> console.log("error"),
16 () => console.log("complete"))
17 }
18
19
20
21 ****Output *****
22 1
23 2
24 3
25 Complete
26
13
8
9 observer.complete()
10 })
11
12 obsUsingConstructor
13 .subscribe(val => console.log(val),
14 error=> console.log("error"),
15 () => console.log("complete"))
16 }
17
18
19 ****Output *****
20 1
21 2
22 3
23 complete
24
Of Operator
The Of creates the observable from the arguments that you pass into it. You can pass any
number of arguments to the Of. Each argument emitted separately and one after the other.
It sends the Complete signal in the end.
14
11
12 **** Output ***
13 [1, 2, 3, 4, 5, 6, 7]
14 complete
15
You can pass more than one array
1
2 ngOnInit() {
3 const array1=[1,2,3,4,5,6,7]
4 const array2=['a','b','c','d','e','f','g']
5 const obsof2=of(array1,array2 );
6 obsof2.subscribe(val => console.log(val),
7 error=> console.log("error"),
8 () => console.log("complete"))
9
10 }
11
12
13 **** Output ***
14 [1, 2, 3, 4, 5, 6, 7]
15 ['a','b','c','d','e','f','g']
16 complete
17
observable from a sequence of numbers
In the following example, we pass 1,2 & 3 as the argument to the from. Each emitted
separately.
1
2 ngOnInit() {
3 const obsof3 = of(1, 2, 3);
4 obsof3.subscribe(val => console.log(val),
5 error => console.log("error"),
6 () => console.log("complete"))
7
8 }
9
10
11
12 **** Output ***
13 1
14 2
15 3
16 complete
17
observable from string
We pass two strings to the of method. Each argument is emitted as it is.
1
2 ngOnInit() {
15
3 const obsof4 = of('Hello', 'World');
4 obsof4.subscribe(val => console.log(val),
5 error => console.log("error"),
6 () => console.log("complete"))
7 }
8
9
10 **** Output ***
11 Hello
12 World
13 complete
14
observable from a value, array & string
We can pass anything to the Of operator. It justs emits it back one after the other.
1
2 ngOnInit() {
3 const obsof5 = of(100, [1, 2, 3, 4, 5, 6, 7],"Hello World");
4 obsof5.subscribe(val => console.log(val),
5 error => console.log("error"),
6 () => console.log("complete"))
7 }
8
9 **** Output ***
10 100
11 [1, 2, 3, 4, 5, 6, 7]
12 Hello World
13 complete
14
From Operator
From Operator takes only one argument that can be iterated and converts it into an
observable.
You can use it to convert
an Array,
anything that behaves like an array
Promise
any iterable object
collections
any observable like object
It converts almost anything that can be iterated to an Observable.
16
To use from you need to import it from rxjs library as shown below.
1
2 import { from } from 'rxjs';
3
observable from an array
The following example converts an array into an observable. Note that each element of the
array is iterated and emitted separately.
1
2 ngOnInit() {
3
4 const array3 = [1, 2, 3, 4, 5, 6, 7]
5 const obsfrom1 = from(array3);
6 obsfrom1.subscribe(val => console.log(val),
7 error => console.log("error"),
8 () => console.log("complete"))
9
10 }
11
12 *** Output ****
13 1
14 2
15 3
16 4
17 5
18 6
19 7
20 complete
21
Observable from string
The from operator iterates over each character of the string and then emits it. The example
is as shown below.
1
2 ngOnInit() {
3 const obsfrom2 = from('Hello World');
4 obsfrom2.subscribe(val => console.log(val),
5 error => console.log("error"),
6 () => console.log("complete"))
7 }
8
9
10 *** Output ****
11 H
12 e
13 l
14 l
15 o
16
17
17 W
18 o
19 r
20 l
21 d
22 complete
23
Observable from collection
Anything that can be iterated can be converted to observable. Here is an example using a
collection.
1
2 ngOnInit() {
3 let myMap = new Map()
4 myMap.set(0, 'Hello')
5 myMap.set(1, 'World')
6 const obsFrom3 = from(myMap);
7 obsFrom3.subscribe(val => console.log(val),
8 error => console.log("error"),
9 () => console.log("complete"))
10 )
11
12 *** output ***
13 [0, "Hello"]
14 [1, "World"]
15 complete
16
Observable from iterable
Any Iterable types like Generator functions can be converted into an observable
using from the operator.
1
2 ngOnInit() {
3 const obsFrom4 = from(this.generateNos())
4 obsFrom4.subscribe(val => console.log(val),
5 error => console.log("error"),
6 () => console.log("complete"))
7 }
8
9 *generateNos() {
10 var i = 0;
11 while (i < 5) {
12 i = i + 1;
13 yield i;
14 }
15
16
17 *** Output ***
18 1
18
19 2
20 3
21 4
22 5
23
Observable from promise
Use it to convert a Promise to an observable
1
2 ngOnInit() {
3 const promiseSource = from(new Promise(resolve => resolve('Hello World!')));
4 const obsFrom5 = from(promiseSource);
5 obsFrom5.subscribe(val => console.log(val),
6 error => console.log("error"),
7 () => console.log("complete"))
8 }
9
10 *** Output ****
11 Hello World
12 complete
13
Of Vs From
Of from
Accepts variable no of arguments Accepts only one argument
emits each argument as it is without changing iterates over the argument and emits each
anything value
Create Observable from Event using FromEvent in Angular
Leave a Comment / March 9, 2023 / 3 minutes of reading
Angular provides FromEvent method to create an observable from DOM events directly. In
this article let us learn how to do that by creating an observable from the button click event,
keyup even & scroll events.
Table of Contents
Syntax
Example of fromEvent
o How it works
fromevent from button click
fromevent from scroll
fromevent from keyup
Reference
Syntax
1
2 fromEvent<T>(target: FromEventTarget<T>,
3 eventName: string,
4 options: EventListenerOptions,
5 resultSelector: (...args: any[]) => T): Observable<T>
6
FromEventTarget is the first argument to fromevent. It can be a DOM EventTarget, Node.js
EventEmitter, JQuery-like event target, NodeList or HTMLCollection. The target must have
19
a method to register/unregister the event handler.
(addEventListener/ removeEventListener in case of DOM Event target)
eventName is the second argument, which is a type of event we want to listen to.
Options are the additional argument that we want to pass to , when registering the event
handler i.e addEventListener
resultSelector is optional and will be deprecated in future versions.
Example of fromEvent
To create an observable from any event, first, we need to get the reference to DOM
element using the viewchild & ElementRef. For example the following code gets the
reference to the button element with the id #btn
1
2 //Template
3 <button #btn>Button</button>
4
1
2 //Component
3
4 @ViewChild('btn', { static: true }) button: ElementRef;
5
The code this.button.nativeElement returns the native DOM element. We pass this as the
first argument to the fromEvent to create an observable to the click event.
1
2 buttonClick() {
3 this.buttonSubscription = fromEvent(this.button.nativeElement, 'click')
4 .subscribe(res => console.log(res));
5 }
6
We can invoke the above method from the ngAfterViewInit method. Note that
the @ViewChildwill not initialize the btn element until the ngOnInit Hence we are using
the ngAfterViewInit here.
1
2 ngAfterViewInit() {
3 this.buttonClick();
4 }
5
6
How it works
When we subscribe to an observable, which we created using the fromEvent method, it
registers the event handler using the addEventListener in the DOM element. Whenever the
user clicks on the button, fromevent captures the value and emits it to the subscriber as the
first argument. When we unsubscribe, it unregisters the event handler using
the removeEventListener.
fromevent from button click
The following is the complete code of fromevent from a button click.
1
2 import { Component, Input, ViewChild, ElementRef, AfterViewInit, OnInit, OnDestroy } from
3 '@angular/core';
20
import { Observable, of, from, fromEvent } from 'rxjs';
4
import { debounceTime } from 'rxjs/operators';
5
6
@Component({
7
selector: 'app-root',
8
templateUrl: './app.component.html',
9
styleUrls: ['./app.component.css']
10
})
11
export class AppComponent implements AfterViewInit , OnInit, OnDestroy {
12
13
title = 'Angular fromEvent Example';
14
15
@ViewChild('btn', { static: true }) button: ElementRef;
16
17
buttonSubscription
18
19
constructor(private elm: ElementRef) {
20
}
21
22
ngOnInit() {
23
}
24
25
26
ngAfterViewInit() {
27
this.buttonClick();
28
}
29
30
31
buttonClick() {
32
this.buttonSubscription = fromEvent(this.button.nativeElement, 'click')
33
.pipe(debounceTime(300))
34
.subscribe(res => console.log(res));
35
}
36
37
38
ngOnDestroy() {
39
this.buttonSubscription.unsubscribe()
40
}
41
42
}
43
21
5 }
6
fromevent from keyup
The following code shows how to create observable from a keyUp event.
1
2 //Component
3
4 @ViewChild('name', { static: true }) name: ElementRef;
5
6 ngAfterViewInit() {
7 fromEvent(this.name.nativeElement, 'keyup')
8 .subscribe(res => console.log(res));
9 }
10
Name
Reference
Using Angular observable pipe with example
5 Comments / March 9, 2023 / 4 minutes of reading
Observable from event
Angular Tutorial
Angular Map
The pipe method of the Angular Observable is used to chain multiple operators together.
We can use the pipe as a standalone method, which helps us to reuse it at multiple places
or as an instance method. In this tutorial, we will take a look at the pipe and learn how to
use it in an Angular Application. We will show you examples
of pipe using map, filter & tap operators.
Table of Contents
RxJs Operators
Using pipe to combine operators
o Pipe as an instance method
o Pipe as stand alone method
References
RxJs Operators
The operators are very important components of the Rxjs library. They are functions that
take an observable as input and transform it into a new observable and return it. We use
them to manipulate the observable data stream.
For Example.
Map operator applies a given project function to each value emitted by the source
Observable and emits the resulting values as an Observable.
Filter operator filter items from the source observable based on some condition and returns
the filtered value as a new observable
22
The following table lists some of the commonly used operators
AREA OPERATORS
Combination combineLatest, concat, merge, startWith , withLatestFrom, zip
Filtering debounceTime,
distinctUntilChanged, filter,
take, takeUntil, takeWhile, takeLast, first, last, single, skip, skipUntil, skipWhile
pLast,
Transformati bufferTime, concatMap, map, mergeMap, scan, switchMap, ExhaustMap, redu
on
Utility tap, delay, delaywhen
Error throwerror, catcherror, retry, retrywhen
Handling
Multicasting share
23
1
2 obs.pipe(
3 op1(),
4 op2(),
5 op3(),
6 op3(),
7 )
8
Example : Pipe with Map, Filter & Tap
Here is the example of using pipe with map & filter operator.
1
2 import { Component, OnInit } from '@angular/core';
3 import { Observable, of} from 'rxjs';
4 import { map, filter, tap } from 'rxjs/operators'
5
6
7 @Component({
8 selector: 'app-root',
9 templateUrl: './app.component.html',
10 styleUrls: ['./app.component.css']
11 })
12 export class AppComponent implements OnInit {
13
14 obs = new Observable((observer) => {
15 observer.next(1)
16 observer.next(2)
17 observer.next(3)
18 observer.next(4)
19 observer.next(5)
20 observer.complete()
21 }).pipe(
22 filter(data => data > 2), //filter Operator
23 map((val) => {return val as number * 2}), //map operator
24 )
25
26 data = [];
27
28 ngOnInit() {
29 this.obs1.subscribe(
30 val => {
31 console.log(this.data)
32 }
33 )
34 }
35
36 }
37
24
38
39 //result
40 [6, 8, 10]
41
The following example makes use of pipe with map, filter & tap operator. The tap operator
returns a new observable which is a mirror copy of the source observable. We use it mostly
for debugging purposes ( for example for logging the values of observable as shown
below).
1
2 import { Component, OnInit } from '@angular/core';
3 import { Observable, of, pipe } from 'rxjs';
4 import { map, filter, tap } from 'rxjs/operators'
5
6
7 @Component({
8 selector: 'app-root',
9 templateUrl: './app.component.html',
10 styleUrls: ['./app.component.css']
11 })
12 export class AppComponent implements OnInit {
13
14 obs = new Observable((observer) => {
15 observer.next(1)
16 observer.next(2)
17 observer.next(3)
18 observer.next(4)
19 observer.next(5)
20 observer.complete()
21 }).pipe(
22 tap(data => console.log('tap '+data)), //tap
23 filter(data => data > 2), //filter
24 tap(data => console.log('filter '+data)), //tap
25 map((val) => { return val as number * 2 }), //map
26 tap(data => console.log('final '+data)), //tap
27 )
28
29
30 data = [];
31
32 ngOnInit() {
33
34 this.obs.subscribe(
35 val => {
36 this.data.push(val)
37 console.log(this.data)
38 }
39 )
25
40
41 }
42 }
43
Pipe as stand alone method
We can also use the pipe as a standalone function to compose operators and re use
the pipe at other places.
Example
1
2 import { Component, OnInit } from '@angular/core';
3 import { Observable, of, pipe } from 'rxjs';
4 import { map, filter, tap } from 'rxjs/operators'
5
6
7 @Component({
8 selector: 'app-root',
9 templateUrl: './app.component.html',
10 styleUrls: ['./app.component.css']
11 })
12 export class AppComponent implements OnInit {
13
14
15 customOperator = pipe(
16 tap(data => console.log('tap '+data)),
17 filter(data => data > 2),
18 tap(data => console.log('filter '+data)),
19 map((val) => {
20 return val as number * 2
21 }),
22 tap(data => console.log('final '+data)),
23 );
24
25
26 obs = new Observable((observer) => {
27 observer.next(1)
28 observer.next(2)
29 observer.next(3)
30 observer.next(4)
31 observer.next(5)
32 observer.complete()
33 }).pipe(
34 this.customOperator,
35 tap(data => console.log('final '+data)),
36 )
37
38
39 data = [];
26
40
41 ngOnInit() {
42
43 this.obs.subscribe(
44 val => {
45 this.data.push(val)
46 console.log(this.data)
47 }
48 )
49
50 }
51 }
52
You can also use the stand alone pipe as shown below.
1
2 customOperator = pipe(
3 tap(data => console.log('tap '+data)),
4 filter(data => data > 2),
5 tap(data => console.log('filter '+data)),
6 map((val) => {
7 return val as number * 2
8 }),
9 tap(data => console.log('final '+data)),
10 );
11
12
13 obs = new Observable((observer) => {
14 observer.next(1)
15 observer.next(2)
16 observer.next(3)
17 observer.next(4)
18 observer.next(5)
19 observer.complete()
20 })
21
22 ngOnInit() {
23 this.customOperator(this.obs).subscribe();
24 }
25
References
1. RxJs Library
2. Operators
3. Pipe API
4. Map API
5. Tap API
6. Filter API
Using Map operator in Angular
27
3 Comments / March 9, 2023 / 4 minutes of reading
Angular Observable pipe
Angular Tutorial
Filter
The Angular observable Map operator takes an observable source as input. It applies a
project function to each of the values emitted by the source observable and transforms it
into a new value. It then emits the new value to the subscribers. We use a Map with a Pipe,
which allows us to chain multiple operators together. In this guide, we’re going to learn how
to use the Map operator with examples like converting the source to upper case, Using Map
the Angular HTTP Request, with DOM events, filtering the input data, and using multiple
Maps together, etc.
Table of Contents
Syntax
Using Observable Map
Map Examples
o Convert input to upper case
o Map to a Single Property
o Using Map with HTTP Request
o Using with event
o map with filter
o Multiple map
References
Syntax
The syntax of the map operator is as follows.
1
map<T, R>(project: (value: T, index: number) => R, thisArg?: any):
2
OperatorFunction<T, R>
3
project: is a function that we use to manipulate the values emitted by the source
observable. The project can accept two arguments. one is value i.e. the value emitted by
the observable. The second argument is index number. The index number starts from 0 for
the first value emitted and incremented by one for every subsequent value emitted. It is
similar to the index of an array.
thisArg: is optional and default is undefined.It defines what this is in the project function.
Using Observable Map
To use map first we need to import it from the rxjs/operators library.
1
2 import { map } from 'rxjs/operators'
3
Next, create an observable from array of numbers as shown below.
1
2 srcArray = from([1, 2, 3, 4]);
3
Use the pipe method to and invoke the map method.
1
2 multiplyBy2() {
28
3 this.srcArray
4 .pipe(map(val => { return val * 2}))
5 .subscribe(val => { console.log(val)})
6 }
7
The project function accepts only one argument val and returns it multiplied by 2.
1
2 map(val => { return val * 2})
3
Finally, we subscribe and print the result in console. The output is 2,4,6,8
The following image explains how values from the source observable ( i.e.1,2,3,4 ) go
through the map which transforms it into new values by multiplying it by 2.
You can also access the second argument index as shown below. It starts as 0 for the first
value and gets incremented for every subsequent value
1
2 multiplyBy2() {
3
4 this.srcArray
5 .pipe(map((val, i) => { //index
6 console.log(i) //0
7 return val * 2;
8 }))
9 .subscribe(val => { console.log(val) })
10 }
11
Map Examples
Convert input to upper case
1
29
2 srcName$ = from(['John', 'Tom', 'Katy'])
3
4 toUpperCase() {
5 this.srcName$
6 .pipe(map(data => {
7 return data.toUpperCase();
8 }))
9 .subscribe(data => console.log(data))
10 }
11
Map to a Single Property
1
2 srcObject = from([
3 { fName: 'Sachin', lName: "Tendulkar" },
4 { fName: 'Rahul', lName: "Dravid" },
5 { fName: 'Saurav', lName: "Ganguly" },
6 ]);
7
8
9 MapToSingleProperty() {
10 this.srcObject
11 .pipe(map(data => { return data.fName + ' ' + data.lName }))
12 .subscribe(data => { console.log(data) })
13 }
14
15 //output
16 Sachin Tendulkar
17 Rahul Dravid
18 Saurav Ganguly
19
Using Map with HTTP Request
The following code gets the list of dogs breeds from
the https://dog.ceo/api/breeds/list/all API and uses the keyValue pipe to transform the
object into an array of key-value pairs.
1
2 import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
3 import { Observable, from, pipe, fromEvent } from 'rxjs';
4 import { map, filter, tap } from 'rxjs/operators'
5 import { HttpClient } from '@angular/common/http';
6 import { KeyValuePipe } from '@angular/common';
7
8 @Component({
9 selector: 'app-root',
10 templateUrl: './app.component.html',
11 styleUrls: ['./app.component.css'],
12 providers: [KeyValuePipe]
13 })
30
14 export class AppComponent implements OnInit {
15
16 constructor(private http: HttpClient,
17 private keyValue: KeyValuePipe) {
18 }
19
20 @ViewChild('btn', { static: true }) button: ElementRef;
21
22 $dogsBreed(): Observable<any> {
23 return this.http.get<any>("https://dog.ceo/api/breeds/list/all")
24 }
25
26 getDogsBreed() {
27
28 this.$dogsBreed()
29 .pipe(map(data => {
30 var dogs = this.keyValue.transform(data.message)
31 console.log(dogs)
32 }))
33 .subscribe();
34
35 }
36 }
37
Using with event
You can create observable from event and use the map to transform the values.
1
2 buttonClick() {
3 fromEvent(this.button.nativeElement, 'click')
4 .pipe(map( ev => (ev as any).clientX))
5 .subscribe(res => console.log(res));
6 }
7
map with filter
1
2 srcArray = from([1, 2, 3, 4]);
3
4 filterWithMap() {
5 this.srcArray
6 .pipe(
7 filter(val => {
8 return val > 2;
9 }),
10 map((val, i) => {
11 return val * 2;
12 }))
13 .subscribe(val => { console.log(val) })
31
14 }
15
Multiple map
The following examples shows use of multiple map functions. The first map adds 10, while
the second mad multiplies by 2.
1
2 mulitpleMaps() {
3 this.srcArray
4 .pipe(
5 map(val => {
6 return val + 10;
7 }),
8 map((val, i) => {
9 return val * 2;
10 }))
11 .subscribe(val => { console.log(val) })
12 }
13
References
Filter Operator in Angular Observable
1 Comment / March 9, 2023 / 2 minutes of reading
Angular Map
Angular Tutorial
Angular Tap
The Filter Operator in Angular filters the items emitted by the source Observable by using a
condition (predicate). It emits only those values, which satisfies the condition and ignores
the rest.
Table of Contents
Filter in Angular
Filter Example
Filter Empty or undefined
Filter object or array
Reference
Filter in Angular
Filter is the simplest and most used RxJs Operator in Angular. The Filter Operator takes 2
arguments.
Syntax
1
filter<T>(predicate: (value: T, index: number) => boolean, thisArg?: any):
2
MonoTypeOperatorFunction<T>
3
The first argument is the predicate function. This function is evaluated against each value of
the source observable. Filter emits only those values which satisfies the the predicate.
The predicate function takes 2 parameters. The first one is the value emitted by the source.
The second argument is zero based index.
32
The second argument thisArg is Optional. It determines the value of this in
the predicate function
Filter Example
The following example filter function returns true only if the value is an even number.
1
2 import { Component } from "@angular/core";
3 import { filter } from "rxjs/operators";
4 import { interval, of, timer } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Filter Example</h1>
10 `,
11 styleUrls: ["./app.component.css"]
12 })
13 export class AppComponent {
14 ngOnInit() {
15 of(1,2,3,4,5,6,7,8,9,10)
16 .pipe(
17 filter(val => {
18 return val %2==0;
19 }),
20 )
21 .subscribe(val => console.log(val));
22 }
23 }
24
25
26 ***Console***
27 2
28 4
29 6
30 8
31 10
32
33
Source Code
Filter Empty or undefined
1
2 filter(value => value !== undefined && value !== null)
3
Filter object or array
1
2 import { Component } from "@angular/core";
3 import { filter } from "rxjs/operators";
4 import { interval, of, timer } from "rxjs";
33
5 import { fromArray } from "rxjs/internal/observable/fromArray";
6
7 @Component({
8 selector: "my-app",
9 template: `
10 <h1>Filter Example</h1>
11 `,
12 styleUrls: ["./app.component.css"]
13 })
14 export class AppComponent {
15 values = [
16 {
17 name: "John",
18 age: 30
19 },
20 {
21 name: "alex",
22 age: 40
23 }
24 ];
25
26 ngOnInit() {
27 fromArray(this.values)
28 .pipe(
29 filter(val => {
30 return val.age > 30;
31 })
32 )
33 .subscribe(val => console.log(val));
34 }
35 }
36
37
Source Code
Reference
Tap operator in Angular observable
Leave a Comment / March 9, 2023 / 3 minutes of reading
Filter
Angular Tutorial
SwitchMap
The Angular Tap RxJs operator returns an observable that is identical to the source. It does
not modify the stream in any way. Tap operator is useful for logging the value, debugging
the stream for the correct values, or perform any other side effects.
Syntax
1
2 tap(nextOrObserver: function, error: function, complete: function): Observable
34
3
Table of Contents
Tap Operator Example
Debugging the Observable
Error & Complete callbacks
Reference
Tap Operator Example
In the following example, we create an observable using the of operator. We use the pipe to
chain the tap operator, which just logs the values of the source observable into the console.
1
2 import { Component, VERSION } from "@angular/core";
3 import { tap } from "rxjs/operators";
4 import { of } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Tap Example</h1>
10 `,
11 styleUrls: ["./app.component.css"]
12 })
13 export class AppComponent {
14 ngOnInit() {
15 of(1, 2, 3, 4, 5)
16 .pipe(
17 tap(val => {
18 console.log("Tap " + val);
19 })
20 )
21 .subscribe(val => console.log("at Subscriber " + val));
22 }
23
24 }
25
26
27
28
29 ***Console
30
31 Tap 1
32 at Subscriber 1
33 Tap 2
34 at Subscriber 2
35 Tap 3
36 at Subscriber 3
37 Tap 4
38 at Subscriber 4
35
39 Tap 5
40 at Subscriber 5
41
42
Source Code
if we simply pass the console.log function to the tap operator and the results will be same.
1
2 of(1, 2, 3, 4, 5)
3 .pipe(tap(console.log))
4 .subscribe(val => console.log("at Subscriber " + val));
5
Tap does not modify the source observable in any way
For Example changing the source any way in the tap operator as in the example below, will
have no effect.
1
2 of(1, 2, 3, 4, 5)
3 .pipe(
4 tap(val => {
5 val=val+1
6 console.log("Tap " + val);
7 return val;
8 })
9 )
10 .subscribe(val => console.log("at Subscriber " + val));
11
Debugging the Observable
One of the use cases for the tap operator is using it to debug the Observable for the correct
values.
The map operator in the following example, adds 5 to the source observable. To debug it,
we can add the two tap operators. One before and one after it and inspect the values.
1
2 of(1, 2, 3, 4, 5)
3 .pipe(
4 tap(val => {
5 console.log("before " +val);
6 }),
7 map(val => {
8 return val + 5;
9 }),
10 tap(val => {
11 console.log("after " +val);
12 })
13 )
14 .subscribe(val => console.log(val));
15
16
17
36
18 **Console**
19 before 1
20 after 6
21 6
22 before 2
23 after 7
24 7
25 before 3
26 after 8
27 8
28
Source Code
Error & Complete callbacks
We can also use the tap operator to log the error and complete callbacks as shown in the
example below.
1
2 import { Component, VERSION } from "@angular/core";
3 import { FormControl, FormGroup } from "@angular/forms";
4 import { debounce, map, tap } from "rxjs/operators";
5 import { interval, of, Subscription } from "rxjs";
6
7 @Component({
8 selector: "my-app",
9 template: `
10 <h1>Tap Example</h1>
11
12
13 `,
14 styleUrls: ["./app.component.css"]
15 })
16 export class AppComponent {
17 ngOnInit() {
18 of(1, 2, 3, 4, 5)
19 .pipe(
20 tap(val => {
21 console.log("Before " + val);
22 }),
23 map(val => {
24 if (val == 3) {
25 throw Error;
26 }
27 return val + 5;
28 }),
29 tap(
30 val => {
31 console.log("After " + val);
32 },
37
33 err => {
34 console.log("Tap Error");
35 console.log(err);
36 },
37 () => {
38 console.log("Tap Complete");
39 }
40 )
41 )
42 .subscribe(val => console.log(val));
43 }
44
45 ngOnDestroy() {}
46 }
47
48
49 ***Console ***
50 Before 1
51 After 6
52 6
53 Before 2
54 After 7
55 7
56 Before 3
57 Tap Error
58 ƒ Error()
59
60 ERROR
61 ƒ Error()
62
Reference
Tap
Using SwitchMap in Angular
11 Comments / March 9, 2023 / 6 minutes of reading
The Angular SwitchMap maps each value from the source observable into an inner
observable, subscribes to it, and then starts emitting the values from it. It creates a new
inner observable for every value it receives from the Source. Whenever it creates a new
inner observable it unsubscribes from all the previously created inner observables.
Basically it switches to the newest observable discarding all other.
Table of Contents
Syntax
SwitchMap Example
SwitchMap Vs Map
SwitchMap switches to the most recent observable
Using SwitchMap in Angular
o With Route Parameters
38
o Angular Forms ValueChanges event
References
Syntax
The syntax of the SwitchMap operator is as shown below.
1
switchMap(project: (value: T, index: number) => O): OperatorFunction<T,
2
ObservedValueOf<O>>
3
project: is a function that we use to manipulate the values emitted by the source
observable. The project can accept two arguments. one is value i.e. the value emitted by
the source observable. The second argument is index number. The index number starts
from 0 for the first value emitted and incremented by one for every subsequent value
emitted. It is similar to the index of an array. The project function must return an
observable.
SwitchMap Example
To use SwitchMap in Angular first we need to import it our Component or Service.
1
2 import { switchMap } from 'rxjs/operators';
3
The following code shows how to use SwitchMap in Angular. We have two
observables srcObservable which emits 1,2,3,4 & innerObservable which
emits 'A','B','C','D'.
39
23 Recd D
24 Source value 2
25 starting new observable
26 Recd A
27 Recd B
28 Recd C
29 Recd D
30 Source value 3
31 starting new observable
32 Recd A
33 Recd B
34 Recd C
35 Recd D
36 Source value 4
37 starting new observable
38 Recd A
39 Recd B
40 Recd C
41 Recd D
42
The project function is the first argument to the switchMap. It takes the values from
the srcObservable. For each value, it receives from the srcObservable (i. e. 1,2,3 &4) it
creates a new observable i.e. innerObservable.
SwitchMap automatically subscribes to the innerObservable returned by the project
function. The innerObservable emits the values (A,B,C,D), and pushes it to the stream
Hence the subscribers will receive the values A, B, C, D four times. Once for each value of
the srcObservable.
SwitchMap Vs Map
The map operators emits value as observable. The SwitchMap creates a inner observable,
subscribes to it and emits its value as observable.
The Following example shows the difference between them.
The map operator below maps the value coming from the source observable to a new value
by multiplying it by 2. It then emits it into the observable stream. The subscribers will
receive the values 2,4,6 & 8.
1
2 let obs= of(1,2,3,4)
3
4 //Using MAP
5 obs.pipe(
6 map(val => {
7 return val*2 //Returning Value
8 })
9 )
10 .subscribe(ret=> {
11 console.log('Recd from map : ' + ret);
12 })
13
40
14 //Output
15 Recd from map : 2
16 Recd from map : 4
17 Recd from map : 6
18 Recd from map : 8
19
We can write the above code using SwitchMap as follows. The only thing that changes is
how we return the new value in the project function. The map example returns the value
as val*2, while the SwitchMap returns new observable (of(val*2)) using the of function. It
also subscribes to the newly created observable and emits its value to the stream.
1
2 let obs= of(1,2,3,4)
3
4 obs.pipe(
5 switchMap( val => {
6 return of(val*2) //Returning observable
7 })
8 )
9 .subscribe(ret=> {
10 console.log('Recd from switchMap : ' + ret);
11 })
12
13 //Output
14 Recd from switchMap : 2
15 Recd from switchMap : 4
16 Recd from switchMap : 6
17 Recd from switchMap : 8
18
SwitchMap switches to the most recent observable
Whenever SwitchMap subscribes to a new inner observable, it unsubscribes from the
previous one.
In the following example, we create an observable from the click event of a button using
the fromEvent method. The SwitchMap operator returns an observable using
the interval method. The interval method creates an infinite observable, which emits a
sequence of integers spaced by a given time interval.
1
2 import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
3 import { of, from, fromEvent, interval, Observable } from 'rxjs';
4 import { switchMap, map, catchError } from 'rxjs/operators';
5
6 @Component({
7 selector: 'app-root',
8 template: `<button #button>Click Me</button>`,
9 })
10 export class AppComponent implements AfterViewInit{
11
12 @ViewChild('button',{static:true}) button;
41
13 clicks$:Observable<any>;
14
15 ngAfterViewInit() {
16 this.clicks$ = fromEvent(this.button.nativeElement, 'click');
17 this.switchExample();
18 }
19
20 switchExample() {
21 this.clicks$
22 .pipe(
23 switchMap(() => {
24 return interval(500)
25 })
26 )
27 .subscribe( val => console.log(val));
28 }
29 }
30
When you click on the button, the clicks observable emits its first value.
The switchMap replaces it with the interval observable, which starts emitting value starting
from 0 every 500ms.
When you click again, the switchMap unsubscribes from the previous interval observable
and starts new one, which again starts to emit value from 0.
42
We can easily solve the above issue using the switchMap. When SwitchMap creates the
second observable it unsubscribes from all the previous observable. Hence even if the
Product 1 data arrives late, it would be discarded as there are no subscribers
1
2 ngOnInit() {
3
4 this.activatedRoute.paramMap
5 .pipe(
6 switchMap((params: Params) => {
7 return this.service.getProduct(params.get('id'))
8 }
9 ))
10 .subscribe((product: Product) => this.product = product);
11 }
12
Angular Forms ValueChanges event
The similar situations described above can also happen when we subscribe to
the ValueChanges event and use that to get data from the back end.
1
2 this.mainForm.get("productCode").valueChanges
3 .pipe(
4 debounceTime(700)
5 )
6 .subscribe(val=> {
7 this.queryDepositData(val)
8 .subscribe(data => {
9 this.product=data;
10 })
11 })
12
The switchMap ensures that only the result from the last observable populates the Product
1
2 this.mainForm.get("productCode").valueChanges
3 .pipe(
4 debounceTime(700),
5 switchMap(val => {
6 return this.queryDepositData();
7 })
8 )
9 .subscribe(data => {
10 this.product=data;
11 })
12
The example also uses the debounceTime operator, which emits a value from the source
Observable only after a particular time span has passed without another source emission.
Using MergeMap in Angular
5 Comments / March 9, 2023 / 6 minutes of reading
43
Angular SwitchMap
Angular Tutorial
Angular ConcatMap
The Angular MergeMap maps each value from the source observable into an inner
observable, subscribes to it, and then starts emitting the values from it replacing the original
value. It creates a new inner observable for every value it receives from the Source.
Unlike SwitchMap, MergeMap does not cancel any of its inner observables. It merges the
values from all of its inner observables and emits the values back into the stream.
Table of Contents
Syntax
MergeMap Example
MergeMap Vs Map
MergeMap combines results of inner observable
Using MergeMap in Angular
o Merging values from two or more HTTP Calls
o Using ForkJoin with MergeMap
References
Syntax
The syntax of the MergeMap operator is as shown below.
1
mergeMap(project: (value: T, index: number) => O): OperatorFunction<T,
2
ObservedValueOf<O>>
3
project: is a function that we use to manipulate the values emitted by the source
observable. The project function accepts two arguments. one is value i.e. the value emitted
by the source observable. The second argument is index number. The index number starts
from 0 for the first value emitted and incremented by one for every subsequent value
emitted. It is similar to the index of an array. The project function must return an
observable.
MergeMap Example
To use MergeMap in Angular first we need to import it our Component or Service.
1
2 import { mergeMap } from 'rxjs/operators';
3
The following code shows how to use MergeMap in Angular. We have two
observables srcObservable which emits 1,2,3,4 & innerObservable which
emits 'A','B','C','D'.
1
2 let srcObservable= of(1,2,3,4)
3 let innerObservable= of('A','B','C','D')
4
5 srcObservable.pipe(
6 mergeMap( val => {
7 console.log('Source value '+val)
8 console.log('starting new observable')
9 return innerObservable
44
10 })
11 )
12 .subscribe(ret=> {
13 console.log('Recd ' + ret);
14 })
15
16
17 //Output
18 Source value 1
19 starting new observable
20 Recd A
21 Recd B
22 Recd C
23 Recd D
24 Source value 2
25 starting new observable
26 Recd A
27 Recd B
28 Recd C
29 Recd D
30 Source value 3
31 starting new observable
32 Recd A
33 Recd B
34 Recd C
35 Recd D
36 Source value 4
37 starting new observable
38 Recd A
39 Recd B
40 Recd C
41 Recd D
42
45
The Map operator below maps the value coming from the source observable to a new value
by multiplying it by 2. It then emits it into the observable stream. The subscribers will
receive the values 2, 4, 6 & 8.
1
2 let obs= of(1,2,3,4)
3
4 //Using MAP
5 obs.pipe(
6 map(val => {
7 return val*2 //Returning Value
8 })
9 )
10 .subscribe(ret=> {
11 console.log('Recd from map : ' + ret);
12 })
13
14 //Output
15 Recd from map : 2
16 Recd from map : 4
17 Recd from map : 6
18 Recd from map : 8
19
We can achieve the same using the MergeMap also. The only thing that changes is how we
return the new value from our project function. The map returns the value as val*2, while
the MergeMap returns the value as observable (of(val*2)) using the of function. It also
subscribes to the newly created observable and emits its value to the stream.
1
2 let obs= of(1,2,3,4)
3
4 obs.pipe(
5 mergeMap( val => {
6 return of(val*2) //Returning observable
7 })
8 )
9 .subscribe(ret=> {
10 console.log('Recd from mergeMap : ' + ret);
11 })
12
13 //Output
14 Recd from mergeMap: 2
15 Recd from mergeMap: 4
16 Recd from mergeMap: 6
17 Recd from mergeMap: 8
18
MergeMap combines results of inner observable
46
MergeMap never cancels any of its inner observable. It waits for them to finish and emit
value. Note that the inner observable’s might finish in an order that is different from the
order in which they are subscribed. MergeMap does not care about the order.
In the following example, we create an observable from the click event of a button using
the fromEvent method. On every click of the button, the MergeMap operator returns an
inner observable delayedObs
The delayedObs emits 5 values separated by 1000 ms.
1
2 import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
3 import { of, from, fromEvent, interval, Observable } from 'rxjs';
4 import { switchMap, map, catchError } from 'rxjs/operators';
5
6 @Component({
7 selector: 'app-root',
8 template: `<button #button>Click Me</button>`,
9 })
10 export class AppComponent implements AfterViewInit{
11
12 @ViewChild('button',{static:true}) button;
13 clicks$:Observable<any>;
14
15 ngAfterViewInit() {
16 this.clicks$ = fromEvent(this.button.nativeElement, 'click');
17 this.mergeMapExample();
18 }
19
20 delayedObs(count:number) {
21 return new Observable((observer) => {
22 setTimeout(() => { observer.next(count+" A") }, 1000);
23 setTimeout(() => { observer.next(count+" B") }, 2000);
24 setTimeout(() => { observer.next(count+" C") }, 3000);
25 setTimeout(() => { observer.next(count+" D") }, 4000);
26 setTimeout(() => { observer.next(count+" E"); observer.complete() }, 5000);
27 })
28 }
29
30 mergeMapExample() {
31
32 let obs=
33
34 this.clicks$
35 .pipe(
36 mergeMap(() => {
37 this.count=this.count+1;
38 return this.delayedObs(this.count)
39 })
40 )
47
41 .subscribe(val => console.log(val));
42 }
43
44 }
45
When you click on the button, the clicks observable emits its first value. Inside
the MergeMap we increase the count by 1 and pass it to the delayedObs.
The mergeMap subscribes to the delayedObs. It starts emitting values A to E prepended by
the count.
When you click again, the mergeMap again subscribes to the delayedObs and it starts to
emit the values again.
The mergeMap collects all the emitted values as they arrive from all of its inner observables
and emits it into the subscribers.
48
7 .subscribe(data => {
8 console.log(data)
9 })
10 })
11
Using ForkJoin with MergeMap
The MergeMap create a one inner observable for each value of outer observable. To
Create more than one inner observable, we can make use of the ForkJoin Operator.
In the following example, along with a list of breeds, we also send a query for a random
image of the dog breed. This requires us to send two HTTP get request in Angular. We
create two observables obs1 & obs2 to do that. Then we use the forJoin to
merge obs1 with obs2 and return a new observable.
1
2 MergeHTTPRequestWithFork() {
3
4 //const url='https://dog.ceo/api/breed/'+hound+'/list';
5
6 of("hound", "mastiff", "retriever")
7 .pipe(
8 mergeMap(breed => {
9 const url1 = 'https://dog.ceo/api/breed/' + breed + '/list';
10 const url2 = 'https://dog.ceo/api/breed/' + breed + '/images/random';
11
12 let obs1= this.http.get<any>(url1)
13 let obs2= this.http.get<any>(url2)
14
15 return forkJoin(obs1,obs2)
16
17 })
18 )
19 .subscribe(data => {
20 console.log(data)
21 })
22
23 }
24
References
Using concatMap in Angular
3 Comments / March 9, 2023 / 7 minutes of reading
Angular MergeMap
Angular Tutorial
Angular exhaustMap
The Angular ConcatMap maps each value from the source observable into an inner
observable, subscribes to it, and then starts emitting the values from it replacing the original
value. It creates a new inner observable for every value it receives from the Source. It
merges the values from all of its inner observables in the order in which they are
49
subscribed and emits the values back into the stream. Unlike SwitchMap, ConcatMap
does not cancel any of its inner observables. It is Similar to MergeMap except for one
difference that it maintains the order of its inner observables.
Table of Contents
Syntax
ConcatMap Example
ConcatMap Vs Map
ConcatMap combines inner observable and keeps the order
Using ConcatMap in Angular
o Merging values from two or more HTTP Calls
o Using ForkJoin with ConcatMap
References
Syntax
The syntax of the concatMap operator is as shown below.
1
concatMap(project: (value: T, index: number) => O): OperatorFunction<T,
2
ObservedValueOf<O>>
3
project: is a function that we use to manipulate the values emitted by the source
observable. The project function accepts two arguments. one is value i.e. the value emitted
by the source observable. The second argument is index number. The index number starts
from 0 for the first value emitted and incremented by one for every subsequent value
emitted. It is similar to the index of an array. The project function must return an
observable.
ConcatMap Example
To use concatMap in Angular first we need to import it our Component or Service.
1
2 import { concatMap } from 'rxjs/operators';
3
In the following code, we have two observables srcObservable which
emits 1,2,3,4 & innerObservable which emits 'A','B','C','D'.
1
2 let srcObservable= of(1,2,3,4)
3 let innerObservable= of('A','B','C','D')
4
5 srcObservable.pipe(
6 concatMap( val => {
7 console.log('Source value '+val)
8 console.log('starting new observable')
9 return innerObservable
10 })
11 )
12 .subscribe(ret=> {
13 console.log('Recd ' + ret);
14 })
15
16
50
17 //Output
18 Source value 1
19 starting new observable
20 Recd A
21 Recd B
22 Recd C
23 Recd D
24 Source value 2
25 starting new observable
26 Recd A
27 Recd B
28 Recd C
29 Recd D
30 Source value 3
31 starting new observable
32 Recd A
33 Recd B
34 Recd C
35 Recd D
36 Source value 4
37 starting new observable
38 Recd A
39 Recd B
40 Recd C
41 Recd D
42
51
1
2 let obs= of(1,2,3,4)
3
4 //Using MAP
5 obs.pipe(
6 map(val => {
7 return val*2 //Returning Value
8 })
9 )
10 .subscribe(ret=> {
11 console.log('Recd from map : ' + ret);
12 })
13
14 //Output
15 Recd from map : 2
16 Recd from map : 4
17 Recd from map : 6
18 Recd from map : 8
19
In the ConcatMap example, only thing that changes is how we return the new value from
our project function. The map returns the value as val*2, while the concatMap returns the
value as observable (of(val*2)) using the of function. It also subscribes to the newly created
observable and emits its value to the stream.
1
2 let obs= of(1,2,3,4)
3
4 obs.pipe(
5 concatMap( val => {
6 return of(val*2) //Returning observable
7 })
8 )
9 .subscribe(ret=> {
10 console.log('Recd from concatMap : ' + ret);
11 })
12
13 //Output
14 Recd from concatMap: 2
15 Recd from concatMap: 4
16 Recd from concatMap: 6
17 Recd from concatMap: 8
18
ConcatMap combines inner observable and keeps the order
ConcatMap never cancels any of its inner observable. It waits for them to finish and emit
value. It also waits for the previous inner observable to finish before creating a new
observable.
52
In the following example, we create an observable from the click event of a button using
the fromEvent method. On every click of the button, the ConcatMap operator returns an
inner observable delayedObs
The delayedObs emits 5 values separated by 1000 ms.
1
2 import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
3 import { of, from, fromEvent, interval, Observable } from 'rxjs';
4 import { switchMap, map, catchError } from 'rxjs/operators';
5
6 @Component({
7 selector: 'app-root',
8 template: `<button #button>Click Me</button>`,
9 })
10 export class AppComponent implements AfterViewInit{
11
12 @ViewChild('button',{static:true}) button;
13 clicks$:Observable<any>;
14
15 ngAfterViewInit() {
16 this.clicks$ = fromEvent(this.button.nativeElement, 'click');
17 this.concatMapExample3();
18 }
19
20 delayedObs(count:number) {
21 return new Observable((observer) => {
22 setTimeout(() => { observer.next(count+" A") }, 1000);
23 setTimeout(() => { observer.next(count+" B") }, 2000);
24 setTimeout(() => { observer.next(count+" C") }, 3000);
25 setTimeout(() => { observer.next(count+" D") }, 4000);
26 setTimeout(() => { observer.next(count+" E"); observer.complete() }, 5000);
27 })
28 }
29
30 concatMapExample3() {
31
32 let obs=
33
34 this.clicks$
35 .pipe(
36 concatMap(() => {
37 this.count=this.count+1;
38 return this.delayedObs(this.count)
39 })
40 )
41 .subscribe(val => console.log(val));
42 }
43
53
44 }
45
When you click on the button, the clicks observable emits its first value. Inside
the concatMap we increase the count by 1 and pass it to the delayedObs.
The concatMap subscribes to the delayedObs. It starts emitting values A to E prepended by
the count.
When you click again, the concatMap checks if the previous observable has finished. If not
it waits for it to finish before subscribing to the delayedObs
The concatMap collects all the emitted values from all of its inner observables and emits it
into the subscribers.
You can verify from the following result that even though we click multiple times on the click
button, the results always appear in the correct order.
54
8 console.log(data)
9 })
10 })
11
Using ForkJoin with ConcatMap
The ConcatMap create a one inner observable for each value of outer observable. To
Create more than one inner observable, we can make use of the ForkJoin Operator.
In the following example, along with a list of breeds, we also send a query for a random
image of the dog breed. This requires us to send two HTTP get request in Angular. We
create two observables obs1 & obs2 to do that. Then we use the forJoin to
merge obs1 with obs2 and return a new observable.
1
2 MergeHTTPRequestWithFork() {
3
4 //const url='https://dog.ceo/api/breed/'+hound+'/list';
5
6 of("hound", "mastiff", "retriever")
7 .pipe(
8 concatMap(breed => {
9 const url1 = 'https://dog.ceo/api/breed/' + breed + '/list';
10 const url2 = 'https://dog.ceo/api/breed/' + breed + '/images/random';
11
12 let obs1= this.http.get<any>(url1)
13 let obs2= this.http.get<any>(url2)
14
15 return forkJoin(obs1,obs2)
16
17 })
18 )
19 .subscribe(data => {
20 console.log(data)
21 })
22
23 }
24
References
1. ConcatMap API
Read More
Using ExhaustMap in Angular
Leave a Comment / March 9, 2023 / 4 minutes of reading
Angular ConcatMap
Angular Tutorial
The Angular ExhaustMap maps each value from the source observable into an inner
observable, subscribes to it. It then starts emitting the values from it replacing the original
value. It then waits for the inner observable to finish. If it receives any new values before
55
the completion of the inner observable it ignores it. It receives a new value after completion
of the inner observable, then it creates a new inner observable. The whole process repeats
itself until the source observable is completes
Table of Contents
Syntax
ExhaustMap Example
ExhaustMap waits for the inner observable to finish
Using ExhaustMap in Angular
References
Syntax
The syntax of the exhaustMap operator is as shown below.
1
exhaustMap(project: (value: T, index: number) => O): OperatorFunction<T,
2
ObservedValueOf<O>>
3
project: is a function that we use to manipulate the values emitted by the source
observable. The project function accepts two arguments. one is value i.e. the value emitted
by the source observable. The second argument is index number. The index number starts
from 0 for the first value emitted and incremented by one for every subsequent value
emitted. It is similar to the index of an array. The project function must return an
observable.
ExhaustMap Example
To use ExhaustMap in Angular first we need to import it our Component or Service.
1
2 import { exhaustMap} from 'rxjs/operators';
3
In the following code, we have two observables srcObservable which
emits 1,2,3,4 & innerObservable which emits 'A','B','C','D'.
56
17 //Output
18 Source value 1
19 starting new observable
20 Recd A
21 Recd B
22 Recd C
23 Recd D
24 Source value 2
25 starting new observable
26 Recd A
27 Recd B
28 Recd C
29 Recd D
30 Source value 3
31 starting new observable
32 Recd A
33 Recd B
34 Recd C
35 Recd D
36 Source value 4
37 starting new observable
38 Recd A
39 Recd B
40 Recd C
41 Recd D
42
The ExhaustMap receives its values from the srcObservable. For each value, it creates a
new observable i.e. innerObservable. It also automatically subscribes to
the innerObservable. The innerObservable emits the values (A, B, C, D), and pushes it to
the subscribers.
The ExhaustMap differs from the MergeMap, SwitchMap & ConcatMap the way it handles
the inner observable. ExhaustMap always waits for the inner observable to finish. It ignores
any value it receives from the source during this period. Any value it receives after the inner
observable is finished is accepted and it creates a new inner observable.
This difference is becomes clear in the next example.
ExhaustMap waits for the inner observable to finish
ExhaustMap creates and waits for inner observable before resuming.
In the following example, we create an observable from the click event of a button using
the fromEvent method. On every click of the button, the ExhaustMap operator returns an
inner observable delayedObs
The delayedObs emits 5 values separated by 1000 ms.
1
2 import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
3 import { of, from, fromEvent, interval, Observable } from 'rxjs';
4 import { switchMap, map, catchError } from 'rxjs/operators';
5
6 @Component({
57
7 selector: 'app-root',
8 template: `<button #button>Click Me</button>`,
9 })
10 export class AppComponent implements AfterViewInit{
11
12 @ViewChild('button',{static:true}) button;
13 clicks$:Observable<any>;
14
15 ngAfterViewInit() {
16 this.clicks$ = fromEvent(this.button.nativeElement, 'click');
17 this.exhaustMapExample3();
18 }
19
20 delayedObs(count:number) {
21 return new Observable((observer) => {
22 setTimeout(() => { observer.next(count+" A") }, 1000);
23 setTimeout(() => { observer.next(count+" B") }, 2000);
24 setTimeout(() => { observer.next(count+" C") }, 3000);
25 setTimeout(() => { observer.next(count+" D") }, 4000);
26 setTimeout(() => { observer.next(count+" E"); observer.complete() }, 5000);
27 })
28 }
29
30 exhaustMapExample3() {
31
32 let obs=
33
34 this.clicks$
35 .pipe(
36 exhaustMap(() => {
37 this.count=this.count+1;
38 return this.delayedObs(this.count)
39 })
40 )
41 .subscribe(val => console.log(val));
42 }
43
44 }
45
When you click on the button, the clicks observable emits its first value. Inside
the exhaustMapwe increase the count by 1 and pass it to the delayedObs.
The exhaustMapsubscribes to the delayedObs. It starts emitting values A to E prepended
by the count.
When you click again, the exhaustMapchecks to see if the previous observable has
finished. If not it ignore the value.
You can verify it from the following result. When we click multiple times, it ignores the
values when the inner observable is running.
58
Using ExhaustMap in Angular
The exhaustMap is useful in scenarios, where you want to prevent submission of duplicate
values
For Example, the user clicking on the Submit button twice will trigger two HTTP calls to the
back end. The ExhaustMap will prevent the creation of new HTTP call until the previous
one finishes
References
Take, TakeUntil, TakeWhile & TakeLast in Angular Observable
1 Comment / March 9, 2023 / 5 minutes of reading
Angular ExhaustMap
Angular Tutorial
The take, takeUntil, takeWhile & takeLast operators allow us to filter out the emitted values
from the observable. The take(n) emits the first n values, while takeLast(n) emits the last n
values. The takeUntil(notifier) keeps emitting the values until it is notified to stop.
takeWhile(predicate) emits the value while values satisfy the predicate. All of the stops
emitting once done.
Table of Contents
Take
TakeUntil
TakeWhile
o TakeWhile Vs Filter
TakeLast
References
Take
Take operator emits the first n number of values before completing. Any reminder values
are ignored.
Syntax
take(n)
Where n is the maximum number of values to emit.
If the source emits more than n values, then take emits only n values and completes
If the source emits less than n number of values, then take emits all of them before
completing.
Example
app.component.ts
1
2 import { Component, VERSION } from "@angular/core";
3 import { of } from "rxjs";
4 import { take } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
9 styleUrls: ["./app.component.css"]
10 })
59
11 export class AppComponent {
12
13 obs = of(1, 2, 3, 4, 5).pipe(take(2));
14
15 ngOnInit() {
16 this.obs.subscribe(val => console.log(val));
17 }
18 }
19
20
21
22 ****Console ******
23 1
24 2
25
Source Code
1
2
3 export class AppComponent {
4
5 takeFive = interval(1000).pipe(take(5));
6
7 ngOnInit() {
8 this.takeFive.subscribe(val => console.log(val));
9 }
10 }
11
12
13 ****Console ******
14 0
15 1
16 2
17 3
18 4
19
TakeUntil
The takeUntil operator returns an Observable that emits value from the source Observable
until the notifier Observable emits a value.
Syntax
1
2 TakeUntil(notifier: Observable): Observable
3
We must pass a notifier observable as the argument to the TakeUntil Operator
TakeUntil emits the values from the Source Observable as long as it does not receive any
value from the notifier observable
When the notifier emits a value, the TakeUntil completes the Source observable.
60
If the notifier completes without emitting any value, then the TakeUntil keeps emitting
values from the source and completes when the source completes.
Example
In the example below, we create a notifier observable
1
2 notifier= new Subject();
3
The notifier observable emits a value , when use clicks on the stop button.
1
2 stopObs() {
3 this.notifier.next();
4 this.notifier.complete();
5 }
6
Use the takeUntil in the source observable as shown below
1
2 obs = interval(1000).pipe(takeUntil(this.notifier));
3
Run the app. The source observable stops when you click on stop button.
app.component.ts
1
2 import { Component, VERSION } from "@angular/core";
3 import { of, interval, Subject, Observable } from "rxjs";
4 import { take, takeUntil, tap } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
9 styleUrls: ["./app.component.css"]
10 })
11 export class AppComponent {
12 notifier = new Subject();
13
14 obs = interval(1000).pipe(takeUntil(this.notifier));
15
16 ngOnInit() {
17 this.obs.subscribe(val => console.log(val));
18 }
19
20 stopObs() {
21 this.notifier.next();
22 this.notifier.complete();
23 }
24 }
25
Source Code
app.component.html
61
1
2 <h1>TakeUntil Example</h1>
3
4 <button (click)="stopObs()">Stop</button>
5 <br>
6
7
Source Code
One of the use cases of takeUntil is to automatically unsubscribe all the observables. You
can refer to it from the tutorial on Unsubscribing from an observable.
TakeWhile
TakeWhile operator will keep emitting the value from the source observable as long as they
pass the given condition (predicate). When it receives a value that does not satisfy the
condition it completes the observable. No further values are emitted even if they satisfy the
condition.
Syntax
1
2 takeWhile(predicate: function(value, index): boolean,
3 inclusive?: boolean): Observable
4
Where predicate is the condition.
If inclusive is true, then the emits the value, which does not pass the condition before
terminating the observable.
Example
In the code below takeWhile tests the condition val < 3 against the incoming values. When
it receives the value 3, which does not satisfy the condition, the observable completes. It
does not emit any further values although the stream still has values that satisfy the
condition.
1
2 import { Component, VERSION } from "@angular/core";
3 import { of, interval, Subject, Observable } from "rxjs";
4 import { take, takeUntil, takeWhile, tap } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
9 styleUrls: ["./app.component.css"]
10 })
11 export class AppComponent {
12 obs = of(1, 2, 3, 1, 2, 3, 1, 2, 3)
13 .pipe(
14 takeWhile(val => val < 3)
15 );
16
17 ngOnInit() {
18 this.obs.subscribe(val => console.log(val));
19 }
62
20 }
21
22
23 *** Console ***
24 1
25 2
26
27
Source Code
With inclusive is set to true, takewhile also emits the value 3 before completing the
observable.
1
2 export class AppComponent {
3 obs = of(1, 2, 3, 1, 2, 3, 1, 2, 3)
4 .pipe(
5 takeWhile(val => val < 3, true)
6 );
7
8 ngOnInit() {
9 this.obs.subscribe(val => console.log(val));
10 }
11 }
12
13 *** Console ***
14 1
15 2
16 3
17
Source Code
Example
1
2 evenNumbers = of(2, 4, 6, 3, 8)
3 .pipe(takeWhile(n => n % 2 == 0))
4 .subscribe(val => console.log(val));
5
6 **Console ***
7 2
8 4
9 6
10
11 //8 is not emitted
12
TakeWhile Vs Filter
Both takeWhile & filter uses the condition to filter out the incoming stream. Both allows only
the matching values to pass through discarding the others.
The difference is that takeWhile discards the rest of the stream, when it receives the first
value that does not satisfy the condition (If the inclusive is set to true, then it also emits the
63
last value even when it does not satisfy the condition). The filter operator never stops the
observable.
The following example, filter operator does not stop when it receives the value 3. But it
discards it and continues further until the stream itself completes.
Example
1
2 obs = of(1, 2, 3, 1, 2, 3, 1, 2, 3)
3 .pipe(
4 filter(val => val < 3, true)
5 );
6
7 ngOnInit() {
8 this.obs.subscribe(val => console.log(val));
9 }
10
11
12 ***Console ****
13 1
14 2
15 1
16 2
17 1
18 2
19
Source Code
TakeLast
TakeLast operator emits the last n number of values from the source observable.
Syntax
1
2 takeLast<T>(n: number)
3
Where n is the maximum number of values to emit.
To know the last n number of values, the TakeLast needs to wait for the source to
complete. Hence if the source never completes, then TakeLast will never emit a value.
When the stream completes, the takeLast will
1. emits the last n number of values
2. if the source emits less than the n number of values then it emits all of them
3. stream completes immediatly
Example
1
2 import { Component, VERSION } from "@angular/core";
3 import { of, range, Observable } from "rxjs";
4 import { takeLast } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
64
9 styleUrls: ["./app.component.css"]
10 })
11 export class AppComponent {
12 obs = range(1, 100).pipe(takeLast(3));
13
14 ngOnInit() {
15 this.obs.subscribe(val => console.log(val));
16 }
17 }
18
19 ***Console****
20 98
21 99
22 100
23
Source Code
References
take
takeUntil
takeWhile
takeLast
First, Last & Single Operator in Angular Observable
1 Comment / March 9, 2023 / 5 minutes of reading
Take, TakeUntil, TakeWhile, TakeLast
Angular Tutorial
In the tutorial, let us learn the First, Last & Single RxJs operators in Angular Observable. All
three operators use the predicate (condition) to check the values of the source observable.
The first emits the first matching value, the Last emits the last matching value & the Single
emits only if a single value matches the predicate.
Table of Contents
First Operator
o First Vs Take(1)
Last Operator
Last Vs TakeLast(1)
Single
References
First Operator
The first operator emits the first value that meets the condition. If no condition is specified,
then it will emit the first value it receives.
Syntax
1
first<T, D>(predicate?: (value: T, index: number, source: Observable<T>) =>
2
boolean, defaultValue?: D) : OperatorFunction<T, T | D>
3
65
Where
predicate: is the condition to match
defaultValue: is the value to emit if no value matches the condition
1. Emits the first value if no predicate is present
2. Emits the first matching value if the predicate is present
3. Closes the stream after emitting a value
4. If the source completes before emitting any matching value, then it raises the error
notification.
Example
In the following example, we create an observable using the of operator. The first operator
here emits the first value it receives i.e 1 and then it completes.
1
2 import { Component, VERSION } from "@angular/core";
3 import { timer, interval, of } from "rxjs";
4 import { first } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
9 styleUrls: ["./app.component.css"]
10 })
11 export class AppComponent {
12 src = of(1, 2, 3, 4, 5).pipe(first());
13
14 id = Date.now();
15
16 constructor() {}
17
18 ngOnInit() {
19 console.log("Component Created " + this.id);
20
21 this.src.subscribe(value => {
22 console.log(value);
23 });
24 }
25 }
26
27
28 *** Result ****
29 1
30
Source Code
Emits the first value that matches
1
2 src = of(1, 2, 3, 4, 5).pipe(first(val => val > 3));
3
4
66
5 **Result**
6 4
7
Source Code
The following code returns an error as no value matches the condition.
1
2 src = of(1, 2, 3, 4, 5).pipe(first(val => val > 10));
3
4
5 ***Error
6 no elements in sequence
7
Source Code
But first with the default value, will emit the default value if no value matches the condition.
1
2 src = of(1, 2, 3, 4, 5).pipe(first(val => val > 10,100));
3
4
5 ***Console**
6 100
7
The following also results in an error, as the source does not emit any values
1
2 src = of().pipe(first());
3
4
5 ***Error
6 no elements in sequence
7
The following emits the default value 100
1
2 src = of().pipe(first(val => true, 100));
3
4
5 ***Console
6 100
7
First Vs Take(1)
The first() (without condition & default value) and take(1) returns the first value they receive
from the source and closes the observable.
But they have a difference in behavior when the source does not emit anything.
The first() send an error notification, while take(1) will not emit anything, but closes the
observable.
Last Operator
The last operator emits the last value that meets the condition. If no condition is specified,
then it will emit the last value it receives.
Syntax
67
1
last<T, D>(predicate?: (value: T, index: number, source: observable<T>) =>
2
boolean, defaultValue?: D) :OperatorFunction<T, T | D>
3
68
4
5 ***Console**
6 2
7
Source Code
Returns an error as no value matches the condition.
1
2 src = of(1, 2, 3, 4, 5).pipe(last(val => val < 0));
3
4
5 *** Console *****
6 ERROR
7 Error: no elements in sequence
8
9
But with a default value, it will emit the default value if no value matches the condition.
1
2 src = of(1, 2, 3, 4, 5).pipe(last(val => val < 0,0));
3
4
5 ***Console**
6 100
7
The following also results in an error, as the source does not emit any values
1
2 src = of().pipe(last());
3
4 ***Error
5 no elements in sequence
6
The following emits the default value 100
1
2 src = of().pipe(last(val => true, 100));
3
4
5 ***Console
6 100
7
Last Vs TakeLast(1)
The last() (without condition & default value) and takeLast(1) returns the last value they
receive from the source observable.
But they have a difference in behavior when the source does not emit anything.
The last() send an error notification, while takeLast(1) will not emit anything, but closes the
observable.
Single
The Single operator emits a single value that meets the condition.
1. The Single operator waits for the source to complete before emitting the value
69
2. Emits a single value that meets the condition
3. If more than one value satisfies the condition, then it raises an error notification
4. If no value satisfies the condition, then it emits the value undefined
5. Raises the error notification, if the source does not emit any value.
Syntax
1
single<T>(predicate?: (value: T, index: number, source: Observable<T>) =>
2
boolean): MonoTypeOperatorFunction<T>
3
Where
predicate is the condition
Examples
Emits 3 as it matches the condition.
1
2 src = of(1, 2, 3, 4, 5).pipe(single(val => val == 3));
3
4
5 **Console
6 3
7
Source Code
Waits for the observable to finish. Hence the following will never emit a value.
1
2 src = interval(1000).pipe(single(val => val == 3));
3
4 **Console*
5
Raises error notification as there are more than one value that satisfies the condition.
1
2 src = of(1, 2, 3, 4, 3).pipe(single(val => val == 3));
3
4
5 **Console**
6 ERROR
7 Sequence contains more than one element
8
9
If the source does not emit any matching values, then the Single operator emits undefined.
Note that it does not emit error notification.
1
2 src = of(1, 2, 3, 4, 3).pipe(single(val => val == 5));
3
4 *Console*
5 undefined
6
Single without any predicate matches all values. Hence the error.
1
70
2 src = of(1, 2, 3, 4, 3).pipe(single());
3
4 ***Console***
5 ERROR
6 Sequence contains more than one element
7
Source contains single value and Single without predicate. Emits the value
1
2 src = of(1).pipe(single());
3
4 **Console**
5 1
6
Source is empty, Hence it raises error notification.
1
2 src = of().pipe(single());
3
4
5 **Console***
6 ERROR
7 Error: no elements in sequence
8
References
1. first
2. last
3. single
Take, TakeUntil, TakeWhile, TakeLast
Angular Tutorial
Angular Tutorial
The skip operators in Angular skips the values from the source observable based on a
condition. The Skip, SkipUntil, SkipWhile skips the values from the start of the source. The
SkipLast Operator skips elements from the end of the source.
Table of Contents
Skip
o Skip Example
SkipWhile
o SkipWhile Example
o Filter Vs SkipWhile
SkipUntil
o SkipUntil Example
SkipLast
o SkipLast Example
71
o SkipLast delays the values
Reference
Skip
The skip operator skips the first count number of values from the source observable and
returns the rest of the source as an observable
Syntax
1
2 skip<T>(count: number): MonoTypeOperatorFunction<T>
3
Skip Example
The example below skip(5) ignores the first 5 values & returns the rest of the observable as
it is.
1
2 import { Component } from "@angular/core";
3 import { skip } from "rxjs/operators";
4 import { interval, of } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Skip Example</h1>
10
11 <br />
12 <br />
13 `,
14 styleUrls: ["./app.component.css"]
15 })
16 export class AppComponent {
17 ngOnInit() {
18 of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
19 .pipe(
20 skip(5),
21 )
22 .subscribe(val => console.log(val));
23 }
24 }
25
26
27 ** Console **
28 6
29 7
30 8
31 9
32 10
33
Source Code
SkipWhile
72
The SkipWhile operator skips values from the source observable as long as the specified
condition is true. But once the condition becomes false, it starts to emit the values and
continues to do so even if the condition becomes true again.
Syntax
1
skipWhile<T>(predicate: (value: T, index: number) => boolean):
2
MonoTypeOperatorFunction<T>
3
SkipWhile Example
1
2 of( 2, 4, 5, 6, 7, 8, 9, 10)
3 .pipe(
4 skipWhile(val => val % 2==0),
5 )
6 .subscribe(val => console.log(val));
7
8
9 **Console**
10 5
11 6
12 7
13 8
14 9
15 10
16
Filter Vs SkipWhile
The Filter operator and SkipWhile operator uses a predicate to filter out the values.
The SkipWhile skips the values if the predicate is true, While the filter emits the values if the
predicate is true.
Once the predicate becomes false, the SkipWhile stops using the predicate and emits all
the remaining values. The filter keeps using the predicate to filter out the remaining values.
Filter Example
1
2 import { Component } from "@angular/core";
3 import { filter } from "rxjs/operators";
4 import { interval, of, timer } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Filter Example</h1>
10 `,
11 styleUrls: ["./app.component.css"]
12 })
13 export class AppComponent {
14 ngOnInit() {
15 of(2, 4, 5, 6, 7, 8, 9, 10)
73
16 .pipe(
17 filter(val => {
18 return val %2==0;
19 }),
20 )
21 .subscribe(val => console.log(val));
22 }
23 }
24
25
26 ***Console***
27 2
28 4
29 6
30 8
31 10
32
SkipUntil
The SkipUntil operator skips the values from the source observable as long as the second
observable does not emit any value. But once the second observable emits a value, it starts
to emit the values and continues to do so as long as the source emits values.
It is very similar to SkipWhile except that the condition is provided by another observable.
Syntax
1
2 skipUntil<T>(notifier: Observable<any>): MonoTypeOperatorFunction<T>
3
SkipUntil Example
The interval creates an observable, which emits a value for every 1000 ms. The SkipUntil
operator uses the timer(6000) observable, which emits a value after 6000ms. Hence the
SkipUntil skips the first 5 values (0 to 4) and starts to emit the values from 5
1
2 interval(1000)
3 .pipe(
4 skipUntil(timer(6000)),
5 )
6 .subscribe(val => console.log(val));
7
8 ***Console **
9 5
10 6
11 7
12 8
13 ....
14
Source Code
SkipLast
74
The SkipLast operator skips the last count number of values from the source observable
and returns the rest of the source as an observable.
It is exactly opposite of skip(count), which skips the first count number of values
Syntax
1
2 skipLast<T>(count: number): MonoTypeOperatorFunction<T>
3
SkipLast Example
In the following example, the skiplast(5) will skip the last 5 values (i.e 6 to 10)
1
2 of(1,2,3,4,5,6,7,8,9,10)
3 .pipe(
4 skipLast(5)
5 )
6 .subscribe(val => console.log(val));
7
8 **Console*
9 1
10 2
11 3
12 4
13 5
14
Source Code
SkipLast delays the values
SkipLast(count) waits until it receives the count number of values from the source, before it
starts emitting the values.
In the above example, the skipLast does not emit any values till the values 5. When it
receives the value 6, it emits the value 1. You can see it by using the tap operator.
1
2 of(1,2,3,4,5,6,7,8,9,10)
3 .pipe(
4 tap(val => {
5 console.log("tap " + val);
6 }),
7 skipLast(5)
8 )
9 .subscribe(val => console.log(val));
10
11
12 **Console**
13 tap 1
14 tap 2
15 tap 3
16 tap 4
17 tap 5
18 tap 6
75
19 1
20 tap 7
21 2
22 tap 8
23 3
24 tap 9
25 4
26 tap 10
27 5
28
Reference
skip
skipUntil
skipWhile
skipLast
The Scan & Reduce operators in Angular
Leave a Comment / March 9, 2023 / 5 minutes of reading
Skip, SkipUntil, SkipWhile & SkipLast
Angular Tutorial
The Scan & Reduce Operators in Angular applies an accumulator function on the values of
the source observable. The Scan Operator returns all intermediate results of the
accumulation, while Reduce only emits the last result. Both also use an optional seed value
as the initial value.
Table of Contents
Scan in Angular
o Scan Example
Reduce in Angular
o Reduce Example
References
Scan in Angular
The scan operator applies an accumulator function over the values emitted by the source
Observableble sequentially and emits each value.
Syntax
1
scan<T, R>(accumulator: (acc: R, value: T, index: number) => R, seed?: T | R):
2
OperatorFunction<T, R>
3
Where
accumulator is the function, that is called on each source value
seed is the initial accumulation value (optional)
The accumulator function gets three argument.
acc is the accumulator variable where the values are accumulated.
value comes from the source observable,
index of the value.
The initial value for the acc comes from the seed.
76
When the first value arrives from the source, the scan operator invokes the accumulator
function on these two variables and emits the result.
When the second value arrives from the source, the result of the previous step becomes
the input ( acc ). The scan emits a new result, which then becomes the input for the third
emission.
This cycle continues till the stream completes.
Scan Example
In the example below (acc, value) => acc + value is the accumulator function. Seed is 0.
1
2 import { Component } from "@angular/core";
3 import { map, scan } from "rxjs/operators";
4 import { interval, of } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Scan Operator Example</h1>
10 `,
11 styleUrls: ["./app.component.css"]
12 })
13 export class AppComponent {
14 reTryCount = 0;
15
16 ngOnInit() {
17 of(1, 2, 3, 4, 5)
18 .pipe(scan((acc, value) => acc + value, 0))
19 .subscribe(
20 val => console.log(val),
21 e => console.log(e),
22 () => console.log("Complete")
23 );
24 }
25 }
26
27
28 ***Console***
29
30 1
31 3
32 6
33 10
34 15
35 Complete
36
Source Code
77
The value for acc starts with the seed value i.e. 0. The variable value gets the value 1,
which is the first value emitted by the source. The scan operator runs the accumulator
function (acc + value = 1) and emits the result.
The result of the previous accumulator function becomes the input (acc) of the next scan. It
is added to the next value (i. e. 2) emitted from the source and the result becomes 3.
This will continue until we reach the end of sequence
In the following code, we change the seed to 10. Now the accumulator starts with 10
instead of 0.
1
2 of(1, 2, 3, 4, 5)
3 .pipe(scan((acc, value) => acc + value, 10))
4 .subscribe(
5 val => console.log(val),
6 e => console.log(e),
7 () => console.log("Complete")
8 );
9
10 *** Result ***
11 11
12 13
13 16
14 20
15 25
16 Complete
17
18
Combining as Arrays
1
2 of(1, 2, 3, 4, 5)
3 .pipe(scan((acc, value) => [...acc, value], []))
4 .subscribe(
5 val => console.log(val),
6 e => console.log(e),
7 () => console.log("Complete")
8 );
9
10 *** Console ***
11 [1]
12 [1, 2]
13 [1, 2, 3]
14 [1, 2, 3, 4]
15 [1, 2, 3, 4, 5]
16
Source Code
Tracking Button Clicks
1
2 import { Component, ElementRef, ViewChild } from "@angular/core";
78
3 import { map, scan } from "rxjs/operators";
4 import { fromEvent, interval, of, Subscription } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Scan Operator Example</h1>
10
11 <button #btn>Button</button>
12 `,
13 styleUrls: ["./app.component.css"]
14 })
15 export class AppComponent {
16 @ViewChild("btn", { static: true }) button: ElementRef;
17 sub: Subscription;
18
19 ngAfterViewInit() {
20 this.sub = fromEvent(this.button.nativeElement, "click")
21 .pipe(scan((acc, value) => acc + 1, 0))
22 .subscribe(val => console.log("You clicked " + val + " times"));
23 }
24
25 ngOnDestroy() {
26 this.sub.unsubscribe();
27 }
28 }
29
30
Source Code
Reduce in Angular
The Reduce operator applies an accumulator function over the values emitted by the
source Observation sequentially and returns the accumulated result when the source
completes.
The Reduce operator behaves exactly like the scan operator, except for the following
differences
1. It does not return the intermediate results.
2. It returns only after the source completes.
Reduce Example
The following example is similar to the scan example above. The only difference is that you
will not see the intermediate results i.e. 1, 3, 6 10.
1
2 ngOnInit() {
3 of(1, 2, 3, 4, 5)
4 .pipe(reduce((acc, value) => acc + value, 0))
5 .subscribe(
6 val => console.log(val),
7 e => console.log(e),
79
8 () => console.log("Complete")
9 );
10 }
11
12
13 ** Console **
14
15 15
16 Complete
17
Source Code
Combining as Arrays
1
2 ngOnInit() {
3 of(1, 2, 3, 4, 5)
4 .pipe(reduce((acc, value) => [...acc, value], []))
5 .subscribe(
6 val => console.log(val),
7 e => console.log(e),
8 () => console.log("Complete")
9 );
10 }
11
12 ** Console ***
13 [1, 2, 3, 4, 5]
14 Complete
15
Source Code
Tracking Button Clicks
The Reduce operator emits only if the observable completes. Hence in the Tracking button
click example, just replacing the scan with the reduce will not work.
In the following example, we create a new observable using the Subject and emit the click
event using the event binding. This allows us to raise the complete notification.
1
2 import { Component, ElementRef, ViewChild } from "@angular/core";
3 import { map, reduce, scan } from "rxjs/operators";
4 import { fromEvent, interval, of, Subject, Subscription } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Reduce Operator Example</h1>
10
11 <button (click)="clickMe($event)">Click Me</button>
12 <br />
13 <br />
14 <br />
80
15 <button (click)="startCounting($event)">Sart</button>
16
17 <button (click)="stopCounting()">Stop</button>
18 `,
19 styleUrls: ["./app.component.css"]
20 })
21 export class AppComponent {
22 clickStream: Subject<Event>;
23 sub: Subscription;
24
25 ngOnInit() {}
26
27 clickMe(event: Event) {
28 console.log("Clicked");
29 if (this.clickStream) this.clickStream.next(event);
30 }
31
32 startCounting(event: Event) {
33 this.clickStream = new Subject<Event>();
34 this.sub = this.clickStream
35 .asObservable()
36 .pipe(reduce((acc, value) => acc + 1, 0))
37 .subscribe(val => console.log("You clicked " + val + " times"));
38 }
39
40 stopCounting() {
41 this.clickStream.complete();
42 }
43
44 ngOnDestroy() {}
45 }
46
47
Source Code
DebounceTime & Debounce in Angular
Leave a Comment / March 9, 2023 / 3 minutes of reading
Scan & Reduce
Angular Tutorial
DebounceTime & Debounce are the Angular RxJs Operators. Both emit values from the
source observable, only after a certain amount of time has elapsed since the last value.
Both emit only the latest value and discard any intermediate values. In this tutorial, we will
learn how to use both DebounceTime & Debounce with examples.
Table of Contents
Use Case of Debounce Operators
DebounceTime
81
o How it works
o DebounceTime Example
o Sending HTTP GET Request
Debounce
o Debounce Example
Reference
Use Case of Debounce Operators
The typeahead/autocomplete fields are one of the most common use cases for Debounce
Operators.
As the user types in the typeahead field, we need to listen to it and send an HTTP request
to the back end to get a list of possible values. If we send HTTP requests for every
keystroke, we end up making numerous unneeded calls to the server.
By using the Debounce Operators, we wait until the user pauses typing before sending an
HTTP Request. This will eliminates unnecessary HTTP requests.
DebounceTime
The Debouncetime emits the last received value from the source observable after a
specified amount of time has elapsed without any other value appearing on the source
Observable
Syntax
1
debounceTime<T>(dueTime: number, scheduler: SchedulerLike = async):
2
MonoTypeOperatorFunction<T>
3
82
styleUrls: ["./app.component.css"]
13
})
14
export class AppComponent {
15
16
mform: FormGroup = new FormGroup({
17
name: new FormControl()
18
});
19
20
obs:Subscription;
21
22
ngOnInit() {
23
this.obs=this.mform.valueChanges
24
.pipe(debounceTime(500))
25
.subscribe(data => console.log(data));
26
}
27
28
ngOnDestroy() {
29
this.obs.unsubscribe();
30
}
31
}
32
Souce Code
Sending HTTP GET Request
The following example shows how you can send an HTTP GET
Request using switchMap & debounceTime.
1
2 ngOnInit() {
3 this.obs=this.mform.valueChanges
4 .pipe(
5 debounceTime(500),
6 switchMap(id => {
7
8 console.log(id)
9 return this.http.get(url)
10
11 })
12 )
13 .subscribe(data => console.log(data));
14 }
15
Debounce
The Debounce operator is similar to the DebounceTime operator, but another observable
will provide the time span. By Using the Debounce, we can set the time span dynamically.
Debounce Example
The following example works the same as the previous example. Instead of using the
hardcoded time span, we create a new observable using the interval operator.
1
83
import { Component, VERSION } from "@angular/core";
2
import { FormControl, FormGroup } from "@angular/forms";
3
import { debounce } from "rxjs/operators";
4
import { interval, Subscription } from "rxjs";
5
6
@Component({
7
selector: "my-app",
8
template: `
9
<form [formGroup]="mform">Name: <input formControlName="name"
10
/></form>
11
`,
12
styleUrls: ["./app.component.css"]
13
})
14
export class AppComponent {
15
mform: FormGroup = new FormGroup({
16
name: new FormControl()
17
});
18
19
obs:Subscription
20
21
ngOnInit() {
22
this.obs=this.mform.valueChanges
23
.pipe(debounce(() => interval(500)))
24
.subscribe(data => console.log(data));
25
}
26
27
ngOnDestroy() {
28
this.obs.unsubscribe()
29
}
30
31
}
32
33
Source Code
You can also customize the delay. In the following example, we increase the delay by
100ms after every keystroke.
1
2 delay = 500;
3
4 obs: Subscription;
5
6 ngOnInit() {
7 this.obs = this.mform.valueChanges
8 .pipe(
9 debounce(() => {
10 this.delay = this.delay + 100;
11 console.log(this.delay);
84
12 return interval(this.delay);
13 })
14 )
15 .subscribe(data => console.log(data));
16 }
17
Source Code
Reference
debounce
debounceTime
Delay & DelayWhen in Angular
Leave a Comment / March 9, 2023 / 3 minutes of reading
DebounceTime & Debounce
Angular Tutorial
ThrowError
Delay & DelayWhen Operators in Angular delays the emission of values from the source
observable. The Delay operator delays by a given timeout or until a given Date. The
DelayWhen delays until it receives a notification from another observable.
Table of Contents
Delay
o Delay Example
o Delay Each item
o Passing Date to Delay Operator
DelayWhen
o DelayWhen Example
References
Delay
Delays the emission of items from the source Observable by a given timeout or until a given
Date.
Syntax
1
delay<T>(delay: number | Date, scheduler: SchedulerLike = async):
2
MonoTypeOperatorFunction<T>
3
Where
delay is the delay in milliseconds or a Date until which the emission of the source items are
delayed
Note that delay delays the entire observable and not the individual items.
Delay Example
In the following example, we add a delay of 1000ms. After 1000ms all the values appear
instantly.
1
2 import { Component, VERSION } from "@angular/core";
3 import { of } from "rxjs";
4 import { delay, map, tap } from "rxjs/operators";
5
85
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Delay & DelayWhen Example</h1>
10 `,
11 styleUrls: ["./app.component.css"]
12 })
13 export class AppComponent {
14 ngOnInit() {
15 of(1, 2, 3, 4, 5)
16 .pipe(
17 tap(val => console.log("Before " + val)),
18 delay(1000)
19 )
20 .subscribe(
21 val => console.log(val),
22 e => console.log(e),
23 () => console.log("Complete")
24 );
25 }
26 }
27
28
29 Before 1
30 Before 2
31 Before 3
32 Before 4
33 Before 5
34
35 1
36 //Appears after a delay of 1000ms
37 2
38 3
39 4
40 5
41 Complete
42
Source Code
Delay Each item
The following code uses concatMap with delay operator to add delay between each
emission.
1
2
3 of(1, 2, 3, 4, 5)
4 .pipe(
5 tap(val => console.log("Before " + val)),
6 concatMap(item => of(item).pipe(delay(1000)))
86
7 )
8 .subscribe(
9 val => console.log(val),
10 e => console.log(e),
11 () => console.log("Complete")
12 );
13
14
15 *** Console ****
16
17 Before 1
18 Before 2
19 Before 3
20 Before 4
21 Before 5
22
23 1
24 2
25 3
26 4
27 5
28 Complete
29
Source Code
Passing Date to Delay Operator
Instead of delay in milliseconds, we can also pass the date. Operator time shifts the start of
the Observable execution until the given date occurs.
In the following example, we add a 5 second to the current date and pass it to the delay
operator.
1
2 import { Component, VERSION } from "@angular/core";
3 import { of } from "rxjs";
4 import { delay, tap } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Delay & DelayWhen Example</h1>
10 `,
11 styleUrls: ["./app.component.css"]
12 })
13 export class AppComponent {
14 dt = new Date();
15
16 ngOnInit() {
17 console.log(this.dt);
18
87
19 this.dt.setSeconds(this.dt.getSeconds() + 5);
20 console.log(this.dt);
21
22 of(1, 2, 3, 4, 5)
23 .pipe(
24 tap(val => console.log("Tap " + val)),
25 delay(this.dt)
26 )
27 .subscribe(
28 val => console.log(val),
29 e => console.log(e),
30 () => console.log("Complete")
31 );
32 }
33 }
34
35
36
Source Code
DelayWhen
Delays the emission of items from the source observable by a given time span determined
by the emissions of another observable.
DelayWhen Example
The DelayWhen triggers when the timer observable emits an value after 1000 ms.
1
2 of(1, 2, 3, 4, 5)
3 .pipe(
4 tap(val => console.log("Before " + val)),
5 delayWhen(() => timer(1000))
6 )
7 .subscribe(
8 val => console.log(val),
9 e => console.log(e),
10 () => console.log("Complete")
11 );
12
13
14 *** Console ****
15 Before 1
16 Before 2
17 Before 3
18 Before 4
19 Before 5
20 1
21 2
22 3
23 4
88
24 5
25 Complete
26
In the following example, we create notification observable using Subject and use it in the
DelayWhen Operator. We emit the notification when the users click on a button. The
DelayWhen waits until the notification before emitting the values
1
2 import { Component, VERSION } from "@angular/core";
3 import { interval, of, Subject, timer } from "rxjs";
4 import { concatMap, delay, delayWhen, map, tap } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Delay & DelayWhen Example</h1>
10
11 <button (click)="emit()">Emit</button>
12 `,
13 styleUrls: ["./app.component.css"]
14 })
15 export class AppComponent {
16 click$ = new Subject<Event>();
17
18 ngOnInit() {
19 of(1, 2, 3, 4, 5)
20 .pipe(
21 tap(val => console.log("Before " + val)),
22 delayWhen(() => this.click$.asObservable())
23 )
24 .subscribe(
25 val => console.log(val),
26 e => console.log(e),
27 () => console.log("Complete")
28 );
29 }
30
31 emit() {
32 this.click$.next();
33 }
34
35 }
36
37
Source Code
References
Delay
delayWhen
89
Using ThrowError in Angular Observable
Leave a Comment / March 9, 2023 / 6 minutes of reading
Delay & DelayWhen
Angular Tutorial
CatchError
90
28
29
30
31 ****Console Window
32
33 Error caught at Subscriber: Error From ThrowError observable
34
Source Code
First, we create an observable using throwError. The first argument to the throwError is the
error object. This error object is passed to the consumers when it raises the error
notification.
1
2 obs = throwError("Error From ThrowError observable")
3
We, subscribe to it in the ngOnInit method.
1
2 this.obs.subscribe(
3
4
The observable immediately raises the error notification and completes. The error callback
is invoked and we will see the error message in the console window.
1
2 err => {
3 console.log("Error caught at Subscriber :" + err);
4 },
5
6
Throw Error Vs ThrowError
It is very easy confuse between the Throw Error With ThrowError.
Throw Error throws an error. It is a JavaScript construct and is not part of the RxJs. We
need to use the try/catch block to catch the errors thrown from the Throw Error. The RxJS
uses the try/catch block to catch any errors thrown from the observables. And when they
catch one, they emit an error notification (raises the error callback), and then the
observable stops.
ThrowError does not throw errors like throw Error. It returns a new observable, which emit
an error notification (raises the error callback), and then stops.
Throw Error Example
1
2 import { Component, VERSION } from "@angular/core";
3 import { Observable, of, from, throwError } from "rxjs";
4 import { map, catchError } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
9 styleUrls: ["./app.component.css"]
10 })
91
11 export class AppComponent {
12
13
14 srcArray = from([1, 2, "A", 4]);
15
16 obs = this.srcArray.pipe(
17 map(val => {
18 let result = (val as number) * 2;
19 if (Number.isNaN(result)) {
20 console.log("Error in the observable");
21 throw Error("Not a Number");
22 }
23 return result;
24 })
25 );
26
27 ngOnInit() {
28 this.obs.subscribe(
29 el => {
30 console.log("Value Received :" + el);
31 },
32 err => {
33 console.log("Error caught at Subscriber :" + err);
34 },
35 () => console.log("Processing Complete.")
36 );
37 }
38 }
39
40
41 ***Console ****
42
43 Value Received :2
44 Value Received :4
45 Error in the observable
46 Error caught at Subscriber :Error: Not a Number
47
Source Code
The observable emits values 2 & 4.
When map operators receive the value A it uses throw Error to throw an error. The
observable catches this error and raises the error notification and terminates.
The last value 8 is never emitted.
ThrowError
Now, let us replace the throw Error with return throwError
1
2 import { Component, VERSION } from "@angular/core";
3 import { Observable, of, from, throwError } from "rxjs";
92
4 import { map, catchError } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
9 styleUrls: ["./app.component.css"]
10 })
11 export class AppComponent {
12 name = "Angular " + VERSION.major;
13
14 srcArray = from([1, 2, "A", 4]);
15
16 obs = this.srcArray.pipe(
17 map(val => {
18 let result = (val as number) * 2;
19 if (Number.isNaN(result)) {
20 console.log("Error in the observable");
21 return throwError("Not a Number");
22 }
23 return result;
24 })
25 );
26
27 ngOnInit() {
28 this.obs.subscribe(
29 (el: any) => {
30 console.log("Value Received :" + el);
31 },
32 err => {
33 console.log("Error caught at Subscriber :" + err);
34 },
35 () => console.log("Processing Complete.")
36 );
37 }
38 }
39
40
41 ****Console ********
42 Value Received :2
43 Value Received :4
44 Error in the observable
45 Value Received :[object Object]
46 Value Received :8
47 Processing Complete
48
49
Source Code
93
The observable emits values 2 & 4.
When the map operator receive the value A it returns throwError.
Remember throwError returns an observable. It will raise the error notification, only if you
subscribe to it.
The map operator does not subscribe to the observable. It just returns it to the subscriber.
Hence the subscriber receives the throwError observable as value. Hence you see [object
Object] in the console.
Since there is no error raised, the observable continues and emits the next value 8 and
then completes.
Using ThrowError
The throwError needs to be subscribed for it to emit error notification. We can use it to
compose with other Observables such as mergeMap, switchMap, catchError etc.
Using with catchError
The following example, shows how to use ThrowError with CatchError
1
2 import { Component, OnInit } from "@angular/core";
3 import { throwError, from } from "rxjs";
4 import { map, catchError } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
9 styleUrls: ["./app.component.css"]
10 })
11 export class AppComponent {
12 srcArray = from([1, 2, "A", 4]);
13
14 obs = this.srcArray.pipe(
15 map(val => {
16 let result = (val as number) * 2;
17 if (Number.isNaN(result)) {
18 console.log("Errors Occurred in Stream");
19 throw new Error("Result is NaN");
20 }
21 return result;
22 }),
23 catchError(error => {
24 console.log("Caught in CatchError. Throwing error");
25 return throwError(error);
26 })
27 );
28
29 ngOnInit() {
30 this.obs.subscribe(
31 el => {
32 console.log("Value Received " + el);
33 },
94
34 err => {
35 console.log("Error caught at Subscriber " + err);
36 },
37 () => console.log("Processing Complete.")
38 );
39 }
40 }
41
42
43 ******* CONSOLE *******
44 Value Received 2
45 Value Received 4
46 Errors Occurred in Stream
47 Caught in CatchError. Throwing error
48 Error caught at Subscriber Error: Result is NaN
49
50
Source Code
The code throws the error using throw error in map operator.
CatchError will catch this error. We use the CatchError to handle the errors thrown by
the Angular Observable. Once we handle the error, we must return an observable. We can
either return a replacement observable or return an error. The observable returned
from CatchError is immediately subscribed.
Hence we can use the throwError here, which is immediately subscribed , which in turn
emits an error notification
1
2 catchError(error => {
3 console.log("Caught in CatchError. Throwing error");
4 return throwError(error);
5 })
6
7
Using it with MergeMap
The Angular MergeMap maps each value from the source observable into an inner
observable, subscribes to it, and then starts emitting the values from it.
In the following example, we use throwError to return a observable, when we receive the
value 3. The MergeMap subscribes to this new observable and raises the error notification
and stops.
1
2 import { Component, OnInit } from "@angular/core";
3 import { throwError, of } from "rxjs";
4 import { map, mergeMap, catchError } from "rxjs/operators";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
9 styleUrls: ["./app.component.css"]
95
10 })
11 export class AppComponent {
12 srcObservable = of(1, 2, 3, 4);
13 innerObservable = of("A", "B");
14
15 obs = this.srcObservable.pipe(
16 mergeMap(val => {
17 console.log("Source value " + val);
18 console.log("starting new observable");
19
20 if (val == 3) return throwError("Error in observable");
21
22
23 return this.innerObservable;
24 })
25 );
26
27 ngOnInit() {
28 this.obs.subscribe(
29 el => {
30 console.log("Value Received " + el);
31 },
32 err => {
33 console.log("Error caught at Subscriber " + err);
34 },
35 () => console.log("Processing Complete.")
36 );
37 }
38 }
39
40
41 ***Console ****
42 Source value 1
43 starting new observable
44 Value Received A
45 Value Received B
46 Source value 2
47 starting new observable
48 Value Received A
49 Value Received B
50 Source value 3
51 starting new observable
52 Error caught at Subscriber Error in observable
53
Source Code
References
Using Catcherror Operator in Angular Observable
96
1 Comment / March 9, 2023 / 6 minutes of reading
ThrowError
Angular Tutorial
Angular CatchError is an RxJs Operator. We can use it to handle the errors thrown by
the Angular Observable. Like all other RxJs operators, the CatchError also takes an
observable as input and returns an observable (or throws an error). We can use CatchError
to provide a replacement observable or throw a user-defined error. Let us learn all these in
this tutorial.
Catch operator was renamed as catchError in RxJs 5.5, Hence if you are using Angular 5 or
prior version then use catch instead of catchError.
Table of Contents
Handling Errors in Observable
o Using Error Callback of Subscribe method
o Catch errors in the observable stream
Using CatchError Operator
o Syntax
o Returning a new observable
o Throws a new Error
o Retrying
References
Handling Errors in Observable
We can handle the errors at two places.
1. Using the error callback of the subscribe method
2. Catch errors in the observable stream
Using Error Callback of Subscribe method
We subscribe to an Observable by using the subscribe method. The subscribe method
accepts three callback methods as arguments. They are the next value, error,
or complete event. We use the error callback to catch & handle the errors.
For Example, consider the following code. The obs observable multiplies the values
(srcArray) by 2 using the map operator. If the result is NaN, then we throw an error
using throw new Error("Result is NaN").
97
12 return result
13 }),
14 );
15
16 ngOnInit() {
17
18 this.obs.subscribe(
19 el => {
20 console.log('Value Received ' + el)
21 },
22 err => {
23 console.log("Error caught at Subscriber " + err)
24 },
25 () => console.log("Processing Complete.")
26 )
27 }
28
29
30 **** Output *****
31 Value Received 2
32 Value Received 4
33 Errors Occurred in Stream
34 Error Caught at subscriber Error: Result is NaN
35
Source Code
We subscribe and start to receive the values from the obs observable in
the ngOnInit method. When the observable stream throws an error, it invokes
the error callback. In the error callback, we decide what to do with the error.
Note that once the observable errors out it will not emit any values neither it calls
the complete callback. Our subscription method will never receive the final value of 8.
Catch errors in the observable stream
Another option to catch errors is to use the CatchError Operator. The CatchError Operators
catches the error in the observable stream as and when the error happens. This allows us
to retry the failed observable or use a replacement observable.
Using CatchError Operator
To use CatchError operator, we need to import it from the rxjs/operators as shown below
1
2 import { catchError } from 'rxjs/operators'
3
Syntax
The catchError is a pipeable operator. We can use it in a Pipe method similar to the other
operators like Map, etc.
The catchError operator gets two argument.
The first argument is err, which is the error object that was caught.
The second argument is caught, which is the source observable. We can return it back
effectively retrying the observable.
The catchError must return a new observable or it can throw an error.
98
Returning a new observable
The following examples shows the use of catchError operator.
1
2 srcArray = from([1, 2, 'A', 4]);
3
4 obs = this.srcArray
5 .pipe(
6 map(val => {
7 let result = val as number * 2;
8 if (Number.isNaN(result)) {
9 console.log('Errors Occurred in Stream')
10 throw new Error("Result is NaN")
11 }
12 return result
13 }),
14 catchError(error => {
15 console.log('Caught in CatchError. Returning 0')
16 return of(0); //return from(['A','B','C'])
17 })
18 );
19
20
21 //Output
22 Value Received 2
23 Value Received 4
24 Errors Occurred in Stream
25 Caught in CatchError. Returning 0
26 Value Received 0
27 Observable Completed
28
Source Code
In the code above, the map emits the values 2 & 4, which is input to the catchError. Since
there are no errors, catchError forwards it to the output. Hence the subscribers receive
values 2 & 4.
The catchError comes into play, when the map operator throws an error.
The catchError handle the error and must return a new observable (or throw an error). In
the example above we return a new observable i.e. of(0). You can also emit any observable
for example return from(['A','B','C']) etc
You can also return the original observable. Just use the return this.obs; instead
of return of(0);. But beware, It will result in an infinite loop.
The new observable is automatically subscribed and the subscriber gets the value 0. The
new observable now finishes and emits the complete event.
Since the original observable ended in a error, it will never emit the the value 8.
Throws a new Error
catchError can also throw an error. In the following example, we use the throw new
Error(error) to throw a JavaScript error. This error will propagate to the subscriber as shown
in the example below.
99
1
2 obs = this.srcArray
3 .pipe(
4 map(val => {
5 let result = val as number * 2;
6 if (Number.isNaN(result)) {
7 console.log('Errors Occurred in Stream')
8 throw new Error("Result is NaN")
9 }
10 return result
11 }),
12 catchError(error => {
13 console.log('Caught in CatchError. Throwing error')
14 throw new Error(error)
15 })
16 );
17
18
19 //OUTPUT
20 Value Received 2
21 Value Received 4
22 Errors Occurred in Stream
23 Caught in CatchError. Throwing error
24 Error caught at Subscriber Error: Error: Result is NaN
25
Source Code
We can also make use of throwError to return an observable. Remember that the
throwError does not throw an error like throw new Error but returns an observable, which
emits an error immediately.
1
2 obs = this.srcArray
3 .pipe(
4 map(val => {
5 let result = val as number * 2;
6 if (Number.isNaN(result)) {
7 console.log('Errors Occurred in Stream')
8 throw new Error("Result is NaN")
9 }
10 return result
11 }),
12 catchError(error => {
13 console.log('Caught in CatchError. Throwing error')
14 return throwError(error);
15 })
16 );
17
18 //OUTPUT
100
19 Value Received 2
20 Value Received 4
21 Errors Occurred in Stream
22 Caught in CatchError. Throwing error
23 Error caught at Subscriber Error: Result is NaN
24
Source code
Retrying
You can also retry the observable using the Retry operator.
1
2 obs = this.srcArray
3 .pipe(
4 map(val => {
5 let result = val as number * 2;
6 if (Number.isNaN(result)) {
7 console.log('Errors Occurred in Stream')
8 throw new Error("Result is NaN")
9 }
10 return result
11 }),
12 retry(2),
13 catchError((error,src) => {
14 console.log('Caught in CatchError. Throwing error')
15 throw new Error(error)
16 })
17 );
18
19
20 //Output
21 Value Received 2
22 Value Received 4
23 Errors Occurred in Stream
24 Value Received 2
25 Value Received 4
26 Errors Occurred in Stream
27 Value Received 2
28 Value Received 4
29 Errors Occurred in Stream
30 Caught in CatchError. Throwing error
31 Error caught at Subscriber Error: Error: Result is NaN
32
Source Code
The catchError gets the source observable as the second argument. If we return it, it will
get subscribed again effectively retrying the observable.
Ensure that you keep track of no of tries so that you can stop the observable after a few
failed attempts. Otherwise, you may run into an infinite loop if the observable always emits
an error.
101
1
2 obs = this.srcArray
3 .pipe(
4 map(val => {
5 let result = val as number * 2;
6 if (Number.isNaN(result)) {
7 console.log('Errors Occurred in Stream')
8 throw new Error("Result is NaN")
9 }
10 return result
11 }),
12 catchError((error,src) => {
13 console.log('Caught in CatchError. Throwing error')
14 this.count++;
15 if (this.count < 2) {
16 return src;
17 } else {
18 throw new Error(error)
19 }
20 })
21 );
22
Source Code
ReTry, ReTryWhen in Angular Observable
1 Comment / March 9, 2023 / 6 minutes of reading
CatchError
Angular Tutorial
Unsubscribe Angular
ReTry & ReTryWhen Operators help us to retry a failed observable in Angular. These
operators are useful in Error handling. They both resubscribe to the source observable
when they receive onError() notification.
Table of Contents
ReTry
o ReTry Example
ReTryWhen
o How it works
o ReTryWhen Example
o Adding a Delay
o Notifier observable
o Limiting Retry Attempts
References
ReTry
The ReTry Angule RxJs operator retries a failed source observable count number of times.
If the count is not provided then it tries indefinitely
Syntax
1
102
2 retry<T>(count: number = -1): MonoTypeOperatorFunction<T>
3
Mirrors the source observable, if there are no errors.
If the source observable calls error notification, it does not propagate it. But re subscribes to
the source again for a maximum of count times.
The Retry operator retries the operation immediately
ReTry Example
In the example below, map operator throws an error if the source emits a value greater than
3.
The ReTry(2) retries the operation twice before erroring out.
1
2 import { Component } from "@angular/core";
3 import { map, retry, tap } from "rxjs/operators";
4 import { interval } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Retry And ReTryWhen Example</h1>
10 `,
11 styleUrls: ["./app.component.css"]
12 })
13 export class AppComponent {
14 ngOnInit() {
15 interval(1000)
16 .pipe(
17 map(val => {
18 if (val > 2) throw new Error("Invalid Value");
19 return val;
20 }),
21 retry(2)
22 )
23 .subscribe(
24 val => console.log(val),
25 err => console.log(err),
26 () => console.log("Complete")
27 );
28 }
29 }
30
31
Source Code
Retry without any argument, will retry indefinitely
1
2 interval(1000)
3 .pipe(
4 map(val => {
103
5 if (val > 2) throw new Error("Invalid Value");
6 return val;
7 }),
8 retry()
9 )
10 .subscribe(val => console.log(val));
11
Retry(0) never retries.
1
2 interval(1000)
3 .pipe(
4 map(val => {
5 if (val > 2) throw new Error("Invalid Value");
6 return val;
7 }),
8 retry(0)
9 )
10 .subscribe(val => console.log(val));
11
ReTryWhen
The RetryWhen Angule RxJs operator retries the failed Observable every time a
Notification Observable emits the next value.
Syntax
1
retryWhen<T>(notifier: (errors: Observable<any>) => Observable<any>):
2
MonoTypeOperatorFunction<T>
3
104
4 import { interval, Observable, observable, of, throwError, timer } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 template: `
9 <h1>Retry And ReTryWhen Example</h1>
10 `,
11 styleUrls: ["./app.component.css"]
12 })
13 export class AppComponent {
14 ngOnInit() {
15 interval(1000)
16 .pipe(
17 map(val => {
18 if (val > 2) throw new Error("Invalid Value");
19 return val;
20 }),
21 retryWhen(
22 error => error.pipe(
23 tap(() => console.log("Retrying... "))))
24 )
25 .subscribe(
26 val => console.log(val),
27 err => console.log(err),
28 () => console.log("Complete")
29 );
30 }
31 }
32
33 **Console **
34
35
36 0
37 1
38 2
39 Retrying..
40 0
41 1
42 2
43 Retrying..
44
Source Code
Returning the error observable as it is also retries the source indefinitely
1
2 interval(1000)
3 .pipe(
4 map(val => {
105
5 if (val > 2) throw new Error("Invalid Value");
6 return val;
7 }),
8 retryWhen(error => error)
9 )
10 .subscribe(
11 val => console.log(val),
12 err => console.log(err),
13 () => console.log("Complete")
14 );
15
Source Code
Adding a Delay
The following code adds delay of 2000 ms.
1
2 interval(1000)
3 .pipe(
4 map(val => {
5 if (val > 2) throw new Error("Invalid Value");
6 return val;
7 }),
8 retryWhen(
9 error =>
10 error.pipe(
11 tap(() => console.log("error occurred ")),
12 delay(2000),
13 tap(() => console.log("Retrying ..."))
14 )
15 )
16 )
17 .subscribe(
18 val => console.log(val),
19 err => console.log(err),
20 () => console.log("Complete")
21 );
22
Source Code
Notifier observable
As long as the notifier observable emits a value, the retryWhen will re subscribes to the
source again.
In the following example, we switch observable to a new observable using SwitchMap
operator. The new observable (of Operator) emits the value 1 after a delay of 1000 ms and
the source is resubscribed again.
1
2 interval(1000)
3 .pipe(
4 map(val => {
106
5 if (val > 2) throw new Error("Invalid Value");
6 return val;
7 }),
8 retryWhen(error =>
9 error.pipe(
10 switchMap(() =>
11 of(1).pipe(
12 delay(1000),
13 tap(() => console.log("of emitted"))
14 )
15 ),
16 tap(() => console.log("Retrying ..."))
17 )
18 )
19 )
20 .subscribe(
21 val => console.log(val),
22 err => console.log(err),
23 () => console.log("Complete")
24 );
25
Source Code
The following uses of() instead of(1). of() does not emit any values but sends a complete
notification on subscription. In this case, RetryWhen does not retry the source but
completes. The subscribers will not receive any notification.
1
2 interval(1000)
3 .pipe(
4 map(val => {
5 if (val > 2) throw new Error("Error: Value greater than 2");
6 return val;
7 }),
8 retryWhen(error =>
9 error.pipe(
10 switchMap(() =>
11 of().pipe(
12 delay(1000),
13 tap(() => console.log("of emitted"))
14 )
15 ),
16 tap(() => console.log("Retrying ..."))
17 )
18 )
19 )
20 .subscribe(
21 val => console.log(val),
22 err => console.error(err),
107
23 () => console.log("Complete")
24 );
25
Source Code
Here is another interesting example where we switch to interval(3000) observable.
When the error occurs the first time in the source, RetryWhen triggers the interval(3000).
When it emits a value source is re-subscribed.
But, the interval(3000) is going to emit another value after 3000ms. In this case, the
RetryWhen will re subscribes to the source again, even if the source has errored out or
already completed.
1
2 interval(1000)
3 .pipe(
4 map(val => {
5 if (val > 2) throw Error;
6 return val;
7 }),
8 retryWhen(error =>
9 error.pipe(
10 tap(() => console.log("error tapped")),
11 switchMap(() =>
12 interval(3000).pipe(tap(() => console.log("interval")))
13 ),
14 tap(() => console.log("Retrying ..."))
15 )
16 )
17 )
18 .subscribe(
19 val => console.log(val),
20 err => console.log(err),
21 () => console.log("Complete")
22 );
23
24 *** Console ***
25 0
26 1
27 2
28 error tapped
29 interval
30 Retrying ...
31 0
32 1
33 interval
34 Retrying ...
35 0
36 1
37 interval
108
38 Retrying ...
39
40
Source Code
Limiting Retry Attempts
In the example below, we use the scan operator to count the number of tries and throw an
error if the number of attempts exceeds 2.
1
2 interval(1000)
3 .pipe(
4 map(val => {
5 if (val > 2) throw new Error("Invalid Value");
6 return val;
7 }),
8 retryWhen(error =>
9 error.pipe(
10 scan((acc, error) => {
11 if (acc > 2) throw error;
12 console.log("attempt " + acc);
13 return acc + 1;
14 }, 1),
15 delay(3000),
16 tap(() => console.log("Retrying ..."))
17 )
18 )
19 )
20 .subscribe(
21 val => console.log(val),
22 err => console.error(err),
23 () => console.log("Complete")
24 );
25
26
27 *** Console ***
28 0
29 1
30 2
31 attempt 1
32 Retrying ...
33 0
34 1
35 2
36 attempt 2
37 Retrying ...
38 0
39 1
40 2
109
41 Invalid Value
42
43
Source Code
Use the dealyWhen to increase the time duration between each retries.
1
2 interval(1000)
3 .pipe(
4 map(val => {
5 if (val > 2) throw new Error("Invalid Value");
6 return val;
7 }),
8 retryWhen(error =>
9 error.pipe(
10 scan((acc, error) => {
11 if (acc > 2) throw error;
12 console.log("attempt " + acc);
13 return acc + 1;
14 }, 1),
15 delayWhen(val => timer(val * 2000)),
16 tap(() => console.log("Retrying ..."))
17 )
18 )
19 )
20 .subscribe(
21 val => console.log(val),
22 err => console.error(err),
23 () => console.log("Complete")
24 );
25
Source Code
You can also use the take or takeWhile operator to stop the retries and emit the complete
notification.
1
2 interval(1000)
3 .pipe(
4 map(val => {
5 if (val > 2) throw new Error("Invalid Value");
6 return val;
7 }),
8 retryWhen(error =>
9 error.pipe(
10 scan((acc, error) => {
11 console.log("attempt " + acc);
12 return acc + 1;
13 }, 1),
14 take(2),
110
15 delayWhen(val => timer(val * 2000)),
16 tap(() => console.log("Retrying ..."))
17 )
18 )
19 )
20 .subscribe(
21 val => console.log(val),
22 err => console.error(err),
23 () => console.log("Complete")
24 );
25 }
26
References
ReTry
ReTryWhen
Unsubscribing from an Observable in Angular
1 Comment / March 9, 2023 / 6 minutes of reading
ReTry & ReTryWhen
Angular Tutorial
Subjects in Angular
112
22 }
23
24 ngOnDestroy() {
25 console.log("Component Destroyed " + this.id);
26 }
27 }
28
29
Source Code
The subscription starts to emit values when the child component is rendered by Angular.
But when we destroy the component, the observable still keeps emitting new values. We
can see this in console window.
If we render the child component again, it starts a new subscription. Now we have two
subscriptions running. As and when we create a new instance of the child component, it will
create a new subscription, and those subscriptions never cleaned up.
How to Unsubscribe
113
Unsubscribing from an observable as easy as calling Unsubscribe() method on the
subscription. It will clean up all listeners and frees up the memory
To do that, first create a variable to store the subscription
1
2 obs: Subscription;
3
Assign the subscription to the obs variable
1
2 this.obs = this.src.subscribe(value => {
3 console.log("Received " + this.id);
4 });
5
6
Call the unsubscribe() method in the ngOnDestroy method.
1
2 ngOnDestroy() {
3 this.obs.unsubscribe();
4 }
5
6
When we destroy the component, the observable is unsubscribed and cleaned up.
The final code is shown below
Child.Component.ts
1
2 import { Component, OnInit } from "@angular/core";
3 import { timer, interval, Subscription } from "rxjs";
4
5 @Component({
6 selector: "app-child-component",
7 templateUrl: "./child.component.html",
8 styleUrls: ["./child.component.css"]
9 })
10 export class ChildComponent implements OnInit {
11 src = interval(2000);
12 id = Date.now();
13 obs: Subscription;
14
15 constructor() {}
16
17 ngOnInit() {
18 console.log("Component Created " + this.id);
19
20 this.obs = this.src.subscribe(value => {
21 console.log("Received " + this.id);
22 });
23 }
24
114
25 ngOnDestroy() {
26 console.log("Component Destroyed " + this.id);
27 this.obs.unsubscribe();
28 }
29 }
30
31
Source Code
When to Unsubscribe
There is no need to unsubscribe from every subscription. For Example, the observables,
which are finite in nature. Although it does not harm if you do so.
In Angular you do not have to Unsubscribe in the following scenarios
The HttpClient Observables like HTTP get & post, etc. They Complete after returning
only one value.
The Angular Router also emits several observables
like paramMap, queryParamMap, fragment, data, URL, Events, etc. The Router
Modules take care of unsubscribing.
All finite observables.
You need to Unsubscribe in the following scenarios
Any Observable that you create in your Angular component or Angular services.
ValueChanges & StatusChanges observables from Angular Forms
The listens to the DOM events from the Renderer2 service
All infinite observables.
When in doubt, always Unsubscribe
Various ways to Unsubscribe
Use Async Pipe
Use Async pipe to subscribe to an observable, it automatically cleans up, when we destroy
the component.
Using Take or First Operator
Convert all infinite observables to finite observable using the Take or First Operators.
The Take Operator emits the first n number of values and then stops the source
observable.
1
2 export class AppComponent {
3
4 obs = of(1, 2, 3, 4, 5).pipe(take(2));
5
6 ngOnInit() {
7 this.obs.subscribe(val => console.log(val));
8 }
9 }
10
11 ****Console ******
12 1
13 2
14
115
The first operator emits the first value and then stops the observable. But It sends an error
notification if does not receive any value.
1
2 export class AppComponent {
3
4 obs = of(1, 2, 3, 4, 5).pipe(first());
5
6 ngOnInit() {
7 this.obs.subscribe(val => console.log(val));
8 }
9 }
10
11 ****Console ******
12 1
13
14
Use Unsubscribe
Using Unsubscribe is the simplest & easiest way.
Store each subscription in a local variable and call unsubscribe on them in
the ngOnDestroy hook
1
2 import { Component, OnInit } from "@angular/core";
3 import { timer, interval, Subscription } from "rxjs";
4
5 @Component({
6 selector: "app-child-component",
7 templateUrl: "./child.component.html",
8 styleUrls: ["./child.component.css"]
9 })
10 export class ChildComponent implements OnInit {
11 src1 = interval(1000);
12 src2 = interval(1500);
13 src3 = interval(2000);
14
15 id = Date.now();
16 obs1: Subscription;
17 obs2: Subscription;
18 obs3: Subscription;
19
20 constructor() {}
21
22 ngOnInit() {
23 console.log("Component Created " + this.id);
24
25 this.obs1 = this.src1.subscribe(value => {
26 console.log("Src1 " + this.id);
27 });
116
28
29 this.obs2 = this.src2.subscribe(value => {
30 console.log("Src2 " + this.id);
31 });
32
33 this.obs3 = this.src3.subscribe(value => {
34 console.log("Src3 " + this.id);
35 });
36 }
37
38 ngOnDestroy() {
39
40 if (this.obs1) this.obs1.unsubscribe();
41 if (this.obs2) this.obs2.unsubscribe();
42 if (this.obs3) this.obs3.unsubscribe();
43
44 console.log("Component Destroyed " + this.id);
45 }
46 }
47
48
Source Code
Using Array to store subscription
Instead of local variable for each subscription, you can create an array and add each
subscription into it
1
2 let subs: Subscription[] = [];
3
Push each subscriptions to array
1
2 this.subs.push(
3 this.src1.subscribe(value => {
4 console.log("Src1 " + this.id);
5 })
6 );
7
In the ngOnDestroy hook, call unsubscribe on each subscriptions
1
2 ngOnDestroy() {
3 this.subs.forEach(sub => sub.unsubscribe());
4
5 console.log("Component Destroyed " + this.id);
6 }
7
Source Code
Using TakeUntil
We can make use of TakeUntil Operator.
117
The takeUntil operator emits values from the source observable until the notifier Observable
emits a value. It then completes the source observable.
To use takeUntil first, we create a notifier observable stop$
1
2 stop$ = new Subject<void>();
3
4
This notifier observable emits a value when the component is destroyed. We do that
in ngOnDestroy hook.
1
2 ngOnDestroy() {
3 this.stop$.next();
4 this.stop$.complete();
5 }
6
7
We add the takeUntil(this.stop$) to all the observable we subscribe. When the component
is destroyed all of them automatically unsubscribed. Remember to add it to the last in
the pipe Operator.
1
2 this.src.pipe(takeUntil(this.stop$)).subscribe(value => {
3 console.log("Obs1 " + this.id);
4 });
5
6
Complete code.
1
2 import { Component, OnInit } from "@angular/core";
3 import { timer, interval, Subscription, Subject } from "rxjs";
4 import { takeUntil } from "rxjs/operators";
5
6 @Component({
7 selector: "app-child-component",
8 templateUrl: "./child.component.html",
9 styleUrls: ["./child.component.css"]
10 })
11 export class ChildComponent implements OnInit {
12 stop$ = new Subject<void>();
13 src = interval(2000);
14
15 id = Date.now();
16
17 constructor() {}
18
19 ngOnInit() {
20 console.log("Component Created " + this.id);
21
118
22 this.src.pipe(takeUntil(this.stop$)).subscribe(value => {
23 console.log("Obs1 " + this.id);
24 });
25
26 this.src.pipe(takeUntil(this.stop$)).subscribe(value => {
27 console.log("Obs2 " + this.id);
28 });
29 }
30
31 ngOnDestroy() {
32 this.stop$.next();
33 this.stop$.complete();
34 console.log("Component Destroyed " + this.id);
35 }
36 }
37
Source Code
Using TakeUntil in a base component
Instead of creating the notifier observable in every component, you can create on in
a BaseComponent and reuse it everywhere.
base.component.ts
1
2 import { Component, OnDestroy } from "@angular/core";
3 import { Subject } from "rxjs";
4
5 @Component({
6 template: ``
7 })
8 export class BaseComponent implements OnDestroy {
9 stop$ = new Subject<void>();
10
11 ngOnDestroy() {
12 this.stop$.next();
13 this.stop$.complete();
14 console.log("BaseComponent Destroyed ");
15 }
16 }
17
18
Extend every component using BaseComponent.
child.component.ts
1
2 import { Component, OnInit } from "@angular/core";
3 import { timer, interval, Subscription, Subject } from "rxjs";
4 import { takeUntil } from "rxjs/operators";
5 import { BaseComponent } from "../base.component";
6
119
@Component({
7
selector: "app-child-component",
8
templateUrl: "./child.component.html",
9
styleUrls: ["./child.component.css"]
10
})
11
export class ChildComponent extends BaseComponent implements OnInit
12
{
13
src = interval(2000);
14
15
id = Date.now();
16
17
constructor() {
18
super();
19
}
20
21
ngOnInit() {
22
console.log("Component Created " + this.id);
23
24
this.src.pipe(takeUntil(this.stop$)).subscribe(value => {
25
console.log("Obs1 " + this.id);
26
});
27
28
this.src.pipe(takeUntil(this.stop$)).subscribe(value => {
29
console.log("Obs2 " + this.id);
30
});
31
}
32
33
ngOnDestroy() {
34
super.ngOnDestroy();
35
console.log("Component Destroyed " + this.id);
36
}
37
}
38
39
Source Code
Note that if you wish you use the constructor or ngOnDestroy in the child component, then
remember to use the super & super.ngOnDestroy() to call the base constructor &
ngOnDestroy of the base component.
Subjects in Angular
4 Comments / March 9, 2023 / 6 minutes of reading
Unsubscribe from Angular
Angular Tutorial
In this tutorial. let us learn RxJs Subject in Angular. The Subjects are special observable
which acts as both observer & observable. They allow us to emit new values to the
observable stream using the next method. All the subscribers, who subscribe to the subject
120
will receive the same instance of the subject & hence the same values. We will also show
the difference between observable & subject. If you are new to observable then refer to our
tutorial on what is observable in angular.
Table of Contents
What are Subjects ?
How does Subjects work
Creating a Subject in Angular
Subject is an Observable
Subject is hot Observable
o Cold observable
o Hot observable
Every Subject is an Observer
Subjects are Multicast
o Multicast vs Unicast
Subjects maintain a list of subscribers
There are other types of subjects
What are Subjects ?
A Subject is a special type of Observable that allows values to be multicasted to many
Observers. The subjects are also observers because they can subscribe to another
observable and get value from it, which it will multicast to all of its subscribers.
Basically, a subject can act as both observable & an observer.
How does Subjects work
Subjects implement both subscribe method and next, error & complete methods. It also
maintains a collection of observers[]
An Observer can subscribe to the Subject and receive value from it. Subject adds them to
its collection observers. Whenever there is a value in the stream it notifies all of its
Observers.
The Subject also implements the next, error & complete methods. Hence it can subscribe to
another observable and receive values from it.
Creating a Subject in Angular
The following code shows how to create a subject in Angular.
app.component.ts
121
1
2 import { Component, VERSION } from "@angular/core";
3 import { Subject } from "rxjs";
4
5 @Component({
6 selector: "my-app",
7 templateUrl: "./app.component.html",
8 styleUrls: ["./app.component.css"]
9 })
10 export class AppComponent {
11
12 subject$ = new Subject();
13
14 ngOnInit() {
15
16 this.subject$.subscribe(val => {
17 console.log(val);
18 });
19
20 this.subject$.next("1");
21 this.subject$.next("2");
22 this.subject$.complete();
23 }
24 }
25
Stackblitz
The code that creates a subject.
1
2 subject$ = new Subject();
3
We subscribe to it just like any other observable.
1
2 this.subject$.subscribe(val => {
3 console.log(val);
4 });
5
The subjects can emit values. You can use the next method to emit the value to its
subscribers. Call the complete & error method to raise complete & error notifications.
1
2 this.subject$.next("1");
3 this.subject$.next("2");
4 this.subject$.complete();
5 //this.subject$.error("error")
6
That’s it.
Subject is an Observable
The subject is observable. It must have the ability to emit a stream of values
122
The previous example shows, we can use the next method to emit values into the stream.
1
2 this.subject$.next("1");
3 this.subject$.next("2");
4
Subject is hot Observable
Observables are classified into two groups. Cold & Hot
Cold observable
The cold observable does not activate the producer until there is a subscriber. This is
usually the case when the observable itself produces the data.
1
2 import { Component, VERSION } from "@angular/core";
3 import { Subject, of } from "rxjs";
4
5 import { Component, VERSION } from "@angular/core";
6 import { Observable, of } from "rxjs";
7
8 @Component({
9 selector: "my-app",
10 templateUrl: "./app.component.html",
11 styleUrls: ["./app.component.css"]
12 })
13 export class AppComponent {
14 obs1$ = of(1, 2, 3, 4);
15
16 obs$ = new Observable(observer => {
17 console.log("Observable starts");
18 observer.next("1");
19 observer.next("2");
20 observer.next("3");
21 observer.next("4");
22 observer.next("5");
23 });
24
25 ngOnInit() {
26 this.obs$.subscribe(val => {
27 console.log(val);
28 });
29 }
30 }
31
32
Stackblitz
The Producer is one that produces the data. In the above example above it is part of the
observable itself. We cannot use that to emit data ourselves.
123
Even in the following code, the producer is part of the observable.
1
2 obs1$ = of(1, 2, 3, 4);
3
The producer produces the value only when a subscriber subscribes to it.
Hot observable
The hot observable does not wait for a subscriber to emit the data. It can start emitting the
values right away. The happens when the producer is outside the observable.
Consider the following example. We have created an observable using subject. In
the ngOnInit, we emit the values & close the Subject. That is without any subscribers.
Since there were no subscribers, no one receives the data. But that did not stop our subject
from emitting data.
1
2 import { Component, VERSION } from "@angular/core";
3 import { Subject } from "rxjs";
4
5 @Component({
6 selector: "my-app",
7 templateUrl: "./app.component.html",
8 styleUrls: ["./app.component.css"]
9 })
10 export class AppComponent {
11
12 subject$ = new Subject();
13
14 ngOnInit() {
15 this.subject$.next("1");
16 this.subject$.next("2");
17 this.subject$.complete();
18 }
19 }
20
21
124
Stackblitz
Now, consider the following example. Here the subjects the values 1 & 2 are lost because
the subscription happens after they emit values.
1
2 import { Component, VERSION } from "@angular/core";
3 import { Subject } from "rxjs";
4
5 @Component({
6 selector: "my-app",
7 templateUrl: "./app.component.html",
8 styleUrls: ["./app.component.css"]
9 })
10 export class AppComponent {
11 subject$ = new Subject();
12
13 ngOnInit() {
14 this.subject$.next("1");
15 this.subject$.next("2");
16
17 this.subject$.subscribe(val => {
18 console.log(val);
19 });
20
21 this.subject$.next("3");
22 this.subject$.complete();
23 }
24 }
25
Stackblitz
Every Subject is an Observer
The observer needs to implement the next, error & Complete callbacks (all optional) to
become an Observer
1
2 import { Component, VERSION } from "@angular/core";
3 import { Observable, Subject } from "rxjs";
4
5 @Component({
6 selector: "my-app",
7 templateUrl: "./app.component.html",
8 styleUrls: ["./app.component.css"]
9 })
10 export class AppComponent {
11 subject$ = new Subject();
12
13 observable = new Observable(observer => {
14 observer.next("first");
15 observer.next("second");
125
16 observer.error("error");
17 });
18
19 ngOnInit() {
20 this.subject$.subscribe(val => {
21 console.log(val);
22 });
23
24 this.observable.subscribe(this.subject$);
25 }
26 }
27
Stackblitz
The following example creates a Subject & and an Observable
We subscribe to the Subject in the ngOnInit method.
We also subscribe to the observable, passing the subject. Since the subject implements the
next method, it receives the values from the observable and emits them to the subscribers.
The Subject here acts as a proxy between the observable & subscriber.
1
2 this.observable.subscribe(this.subject$);
3
Subjects are Multicast
Another important distinction between observable & subject is that subjects are multicast.
More than one subscriber can subscribe to a subject. They will share the same instance of
the observable. This means that all of them receive the same event when the subject emits
it.
Multiple observers of an observable, on the other hand, will receive a separate instance of
the observable.
Multicast vs Unicast
The Subjects are multicast.
Consider the following example, where we have an observable and subject defined. The
observable generates a random number and emits it.
1
2 import { Component, VERSION } from "@angular/core";
3 import { from, Observable, of, Subject } from "rxjs";
4
5 @Component({
6 selector: "my-app",
7 templateUrl: "./app.component.html",
8 styleUrls: ["./app.component.css"]
9 })
10 export class AppComponent {
11 observable$ = new Observable<number>(subscriber => {
12 subscriber.next(Math.floor(Math.random() * 200) + 1);
13 });
14
15 subject$ = new Subject();
126
16
17 ngOnInit() {
18 this.observable$.subscribe(val => {
19 console.log("Obs1 :" + val);
20 });
21
22 this.observable$.subscribe(val => {
23 console.log("Obs2 :" + val);
24 });
25
26 this.subject$.subscribe(val => {
27 console.log("Sub1 " + val);
28 });
29 this.subject$.subscribe(val => {
30 console.log("Sub2 " + val);
31 });
32
33 this.subject$.next(Math.floor(Math.random() * 200) + 1);
34 }
35 }
36
Stackblitz
We have two subscribers of the observable. Both these subscribers will get different values.
This is because observable on subscription creates a separate instance of the producer.
Hence each one will get a different random number
1
2 this.observable$.subscribe(val => {
3 console.log("Obs1 :" + val);
4 });
5
6 this.observable$.subscribe(val => {
7 console.log("Obs2 :" + val);
8 });
9
Next, we create two subscribers to the subject. The subject emits the value using the
random number. Here both subscribets gets the same value.
1
2 this.subject$.subscribe(val => {
3 console.log("Sub1 " + val);
4 });
5 this.subject$.subscribe(val => {
6 console.log("Sub2 " + val);
7 });
8
9 this.subject$.next(Math.floor(Math.random() * 200) + 1);
10
Subjects maintain a list of subscribers
127
Whenever a subscriber subscribes to a subject, it will add it to an array of subscribers. This
way Subject keeps track of its subscribers and emits the event to all of them.
There are other types of subjects
What we have explained to you is a simple subject. But there are few other types of
subjects. They are
1. ReplaySubject
2. BehaviorSubject
3. AsyncSubject
We will talk about them in the next tutorial.
Unsubscribe from Angular
Angular Tutorial
128
22 console.log("sub2 " + val);
23 });
24
25 this.subject$.next("3");
26 this.subject$.complete();
27 }
28 }
29
30
31
32 ***Result***
33
34 Sub1 0
35 Sub1 1
36 Sub1 2
37 sub2 2
38 Sub1 3
39 sub2 3
40
41
StackBlitz
We create a new BehaviorSubject providing it an initial value or seed value. The
BehaviorSubject stores the initial value.
1
2 subject$ = new BehaviorSubject("0");
3
As soon as the first subscriber subscribes to it, the BehaviorSubject emits the stored value.
i.e. 0
1
2 this.subject$.subscribe(val => {
3 console.log("Sub1 " + val);
4 });
5
We emit two more values. The BehaviorSubject stores the last value emitted i.e. 2
1
2 this.subject$.next("1");
3 this.subject$.next("2");
4
Now, Subscriber2 subscribes to it. It immediately receives the last value stored i.e. 2
1
2 this.subject$.subscribe(val => {
3 console.log("sub2 " + val);
4 });
5
ReplaySubject
ReplaySubject replays old values to new subscribers when they first subscribe.
129
The ReplaySubject will store every value it emits in a buffer. It will emit them to the new
subscribers in the order it received them. You can configure the buffer using the
arguments bufferSize and windowTime
bufferSize: No of items that ReplaySubject will keep in its buffer. It defaults to infinity.
windowTime: The amount of time to keep the value in the buffer. Defaults to infinity.
Example
1
2
3 import { Component, VERSION } from "@angular/core";
4 import { ReplaySubject, Subject } from "rxjs";
5
6 @Component({
7 selector: "my-app",
8 templateUrl: "./app.component.html",
9 styleUrls: ["./app.component.css"]
10 })
11 export class AppComponent {
12 subject$ = new ReplaySubject();
13
14 ngOnInit() {
15 this.subject$.next("1");
16 this.subject$.next("2");
17
18 this.subject$.subscribe(
19 val => console.log("Sub1 " + val),
20 err => console.error("Sub1 " + err),
21 () => console.log("Sub1 Complete")
22 );
23
24 this.subject$.next("3");
25 this.subject$.next("4");
26
27 this.subject$.subscribe(val => {
28 console.log("sub2 " + val);
29 });
30
31 this.subject$.next("5");
32 this.subject$.complete();
33
34 this.subject$.error("err");
35 this.subject$.next("6");
36
37 this.subject$.subscribe(
38 val => {
39 console.log("sub3 " + val);
40 },
41 err => console.error("sub3 " + err),
130
42 () => console.log("Complete")
43 );
44 }
45 }
46
47 ***Result***
48 Sub1 1
49 Sub1 2
50 Sub1 3
51 Sub1 4
52 sub2 1
53 sub2 2
54 sub2 3
55 sub2 4
56 Sub1 5
57 sub2 5
58 Sub1 Complete
59 sub3 1
60 sub3 2
61 sub3 3
62 sub3 4
63 sub3 5
64 sub3 err
65
Stackblitz
First, we create a ReplaySubject
1
2 subject$ = new ReplaySubject();
3
ReplaySubject emits two values. It will also store these in a buffer.
1
2 this.subject$.next("1");
3 this.subject$.next("2");
4
We subscribe to it. The observer will receive 1 & 2 from the buffer
1
2 this.subject$.subscribe(
3 val => console.log("Sub1 " + val),
4 err => console.error("Sub1 " + err),
5 () => console.log("Sub1 Complete")
6 );
7
We subscribe again after emitting two more values. The new subscriber will also receive all
the previous values.
1
2 this.subject$.next("3");
3 this.subject$.next("4");
131
4
5 this.subject$.subscribe(val => {
6 console.log("sub2 " + val);
7 });
8
We emit one more value & complete. All the subscribers will receive complete. They will not
receive any further values or notifcations.
1
2 this.subject$.next("5");
3 this.subject$.complete();
4
5
We now fire an error notification and a value. None of the previous subscribers will receive
this as they are already closed.
1
2 this.subject$.error("err");
3 this.subject$.next("6");
4
Now, we subscribe again. The subscriber will receive all the values up to Complete. But will
not receive the Complete notification, instead, it will receive the Error notification.
1
2 this.subject$.subscribe(
3 val => {
4 console.log("sub3 " + val);
5 },
6 err => console.error("sub3 " + err),
7 () => console.log("Complete")
8 );
9
AsyncSubject
AsyncSubject only emits the latest value only when it completes. If it errors out then it will
emit an error, but will not emit any values.
1
2 import { Component, VERSION } from "@angular/core";
3 import { AsyncSubject, Subject } from "rxjs";
4
5 @Component({
6 selector: "my-app",
7 templateUrl: "./app.component.html",
8 styleUrls: ["./app.component.css"]
9 })
10 export class AppComponent {
11 subject$ = new AsyncSubject();
12
13 ngOnInit() {
14 this.subject$.next("1");
15 this.subject$.next("2");
132
16
17 this.subject$.subscribe(
18 val => console.log("Sub1 " + val),
19 err => console.error("Sub1 " + err),
20 () => console.log("Sub1 Complete")
21 );
22
23 this.subject$.next("3");
24 this.subject$.next("4");
25
26 this.subject$.subscribe(val => {
27 console.log("sub2 " + val);
28 });
29
30 this.subject$.next("5");
31 this.subject$.complete();
32
33 this.subject$.error("err");
34
35 this.subject$.next("6");
36
37 this.subject$.subscribe(
38 val => console.log("Sub3 " + val),
39 err => console.error("sub3 " + err),
40 () => console.log("Sub3 Complete")
41 );
42 }
43 }
44
45
46 **Result **
47 Sub1 5
48 sub2 5
49 Sub1 Complete
50 Sub3 5
51 Sub3 Complete
52
Stackblitz
In the above example, all the subscribers will receive the value 5 including those who
subscribe after the complete event.
But if the AsyncSubject errors out, then all subscribers will receive an error notification and
no value.
Reference
1. https://rxjs-dev.firebaseapp.com/api/index/class/AsyncSubject
2. https://rxjs-dev.firebaseapp.com/api/index/class/BehaviorSubject
3. https://rxjs-dev.firebaseapp.com/api/index/class/ReplaySubject
Read More
133
Angular Tutorial
Angular observable
Subjects in Angular
Angular Observable Subject Example Sharing Data Between Components
1 Comment / March 9, 2023 / 4 minutes of reading
ReplaySubject, BehaviorSubject & AsyncSubject
Angular Tutorial
In this tutorial, we will show you how to use Subjects in Angular with examples. We learned
What is Subjects in Angular and different types of subjects
like ReplaySubject, BehaviorSubject & AsyncSubject in Angular
Table of Contents
Angular Subject Example
Todo Service Using BehaviorSubject
TodoList Component
TodoAdd Component
References
Angular Subject Example
We will build a todo app. Our app has two components.
One is the Todo list component, which displays the list of todos. It also has the option to
delete a todo
Another one is Todo add a component, which allows us to add a Todo item.
Both will communicate with each other via Service. Whenever a new todo is added, the
service will notify the Todo list component to update the lists using a observable
Code is available at StackBlitz
Todo Service Using BehaviorSubject
Create the todo.service.ts in the src\app folder.
todo.service.ts
1
2 import { Injectable } from "@angular/core";
3 import { BehaviorSubject } from "rxjs";
4
5 export interface Todo {
6 id: any;
7 value: string;
8 }
9
10 @Injectable()
11 export class TodoService {
12
13 private _todo = new BehaviorSubject<Todo[]>([]);
14 readonly todos$ = this._todo.asObservable();
15
16 private todos: Todo[] = [];
17 private nextId = 0;
18
134
19 constructor() {}
20
21 loadAll() {
22 this.todos = [];
23 this._todo.next(this.todos);
24 }
25
26 create(item: Todo) {
27 item.id = ++this.nextId;
28 this.todos.push(item);
29 this._todo.next(Object.assign([], this.todos));
30 }
31
32 remove(id: number) {
33 this.todos.forEach((t, i) => {
34 if (t.id === id) {
35 this.todos.splice(i, 1);
36 }
37 this._todo.next(Object.assign([], this.todos));
38 });
39 }
40 }
41
42
Here, we create BehaviorSubject of type Todo[]. Behavior expects us to provide an initial
value. We assign an empty array. The BehaviorSubject will always emit the latest list
of Todo items as an array. We can also use Subject here. But the advantage
of BehaviorSubject is that the late subscribers will always receive the latest list of Todo
items immediately on the subscription. We do not have to call the next method.
1
2 private _todo$ = new BehaviorSubject<Todo[]>([]);
3
4
Also, it is advisable not to expose the BehaviorSubject outside the service. Hence we
convert it to normal Observable and return it. This is because the methods
like next, complete or error do not exist on normal observable. It will ensure that the end-
user will not accidentally call them and mess up with it
1
2 readonly todos$ = this._todo.asObservable();
3
The todos will store the todo items in memory. We use the nextId to create the id of the
Todo item.
1
2 private todos: Todo[] = [];
3 private nextId = 0;
4
135
Create pushes the new item to the todos list. Here we use the next method to push the
todos list to all the subscribers. Note that we use the Object.assign to create a new copy of
the todos and push it to the subscribers. This will protect our original copy of the todos list
from accidental modification by the user.
1
2 create(item: Todo) {
3 item.id = ++this.nextId;
4
5 //Update database
6 this.todos.push(item);
7 this._todo$.next(Object.assign([], this.todos));
8 }
9
Remove method removes the Todo item based on id and pushes the new list to
subscribers.
1
2 remove(id: number) {
3 this.todos.forEach((t, i) => {
4 if (t.id === id) {
5 this.todos.splice(i, 1);
6 }
7 this._todo$.next(Object.assign([], this.todos));
8 });
9 }
10
TodoList Component
TodoListComponent displays the list of Todo items.
todo-list.component.ts
1
2 import { Component, OnInit } from "@angular/core";
3 import { FormBuilder, FormGroup, Validators } from "@angular/forms";
4 import { Observable } from "rxjs";
5 import { map } from "rxjs/operators";
6
7 import { Todo, TodoService } from "./todo.service";
8
9 @Component({
10 selector: "app-todo",
11 template: `
12 <div *ngFor="let todo of (todos$ | async)">
13 {{ todo.id }} {{ todo.value }}
14 <button (click)="deleteTodo(todo.id)">x</button>
15 </div>
16 `
17 })
18 export class TodoListComponent implements OnInit {
19 todos$: Observable<Todo[]>;
136
20
21 constructor(private todoService: TodoService) {}
22
23 ngOnInit() {
24 this.todos$ = this.todoService.todos$;
25 }
26
27 deleteTodo(todoId: number) {
28 this.todoService.remove(todoId);
29 }
30 }
31
32
We inject todoService
1
2 constructor(private todoService: TodoService) {}
3
And get hold of a reference to the todo$ observable.
1
2 this.todos$ = this.todoService.todos$;
3
We use the async pipes to subscribe to the todos$ observable. No need to worry
about unsubscribing the observable as angular handles it when using async pipes
1
2 <div *ngFor="let todo of (todos$ | async)">
3 {{ todo.id }} {{ todo.value }}
4 <button (click)="deleteTodo(todo.id)">x</button>
5 </div>
6
deleteTodo deletes the Todo item by calling the remove method of the todoService
1
2 deleteTodo(todoId: number) {
3 this.todoService.remove(todoId);
4 }
5
TodoAdd Component
We use TodoAddComponent to add a new Todo item
todo-add.component.ts
1
2 import { Component, OnInit } from "@angular/core";
3 import { FormBuilder, FormGroup, Validators } from "@angular/forms";
4 import { Observable } from "rxjs";
5 import { map } from "rxjs/operators";
6
7 import { Todo, TodoService } from "./todo.service";
8
9 @Component({
137
selector: "app-todo-add",
10
template: `
11
<div>
12
<form [formGroup]="todoForm" (submit)="onSubmit()">
13
<p>
14
<input type="text" id="value" name="value"
15
formControlName="value" />
16
</p>
17
18
<button type="submit">Add Item</button><br />
19
</form>
20
</div>
21
`
22
})
23
export class TodoAddComponent implements OnInit {
24
todos$: Observable<Todo[]>;
25
todoForm: FormGroup;
26
27
constructor(
28
private todoService: TodoService,
29
private formBuilder: FormBuilder
30
){
31
this.todoForm = this.formBuilder.group({
32
id: [""],
33
value: ["", Validators.required]
34
});
35
}
36
37
ngOnInit() {
38
this.todos$ = this.todoService.todos$;
39
}
40
41
onSubmit() {
42
this.todoService.create(this.todoForm.value);
43
this.todoForm.get("value").setValue("");
44
}
45
}
46
47
138
We get hold of the todos$ observable. We are not doing anything with it But you can
subscribe to it to get the latest list of Todo items.
1
2 ngOnInit() {
3 this.todos$ = this.todoService.todos$;
4 }
5
onSubmit method creates a new Todo item by calling the create method of the todoService.
1
2 onSubmit() {
3 this.todoService.create(this.todoForm.value);
4 this.todoForm.get("value").setValue("");
5 }
6
app.component.html
1
2 <app-todo-add></app-todo-add>
3 <app-todo></app-todo>
4
Angular as a framework provides us with a significant number of tools and capabilities out of the
box. Today I will be writing about one of these beneficial features called HTTP Interceptors.
I will briefly describe what Angular HTTP Interceptors are and how they work. I will then provide
some common usages with implementation examples and discuss some advantages of using
interceptors on your app. This article assumes that the reader already has some Angular
experience and is comfortable around the most common and basic notions. These will not be
explained in detail since they are not in the scope of this document.
Although the name might sound like something extraordinarily fancy and complicated, Angular
interceptors are merely a special kind of HTTP client service that has the sole purpose of
intercepting every HTTP request performed. This is true for both incoming and outgoing HTTP
requests. OK, I’ve seen this quick definition in several places, but what does that exactly mean?
How does it work?
We all know a picture is worth a thousand words so let’s try and create a simple diagram that will
explain what intercepting a request means:
139
A typical workflow of an Angular app, at any point in time, will perform a series of HTTP requests
to a server to perform everyday tasks. These are authentication, data loading, etc. We will come
back to this in a moment.
The diagram above shows that the HTTP interceptors will always be in the middle of any single
HTTP request. These services will intercept all requests performed by the app, allowing us to
perform many operations on them before they are sent to the server. Functions include adding a
custom HTTP header to the final outgoing request (e.g. adding an authorization header and
passing an authorization token on all endpoints requiring a set of permissions, etc.), caching,
logging to collect metrics, error handling, etc.
A similar process happens when the server replies. We now have the response being intercepted
by the HTTP interceptor, allowing us to perform a series of operations before the app consumes
the final answer. An everyday use case scenario could be transforming the response object into a
format more meaningful to the product. For example, cleaning up the response object and
extracting only the required parts instead of dealing with that on every component that would use
the data.
OK, I guess by now, it’s clear what an HTTP interceptor is, where it sits on an everyday Angular
app workflow, and its purpose. But how does this work? Don’t we risk having multiple requests
modified all over the place and causing a chaotic set of events going back and forth?
Setting up a piece of logic that can transform HTTP requests in a centralized place sounds like a
great feature. This way, we don’t have to create several layers of duplication whenever we want to
perform a request or consume a response. Without interceptors, we would need to implement the
same logic repeatedly for each HTTP request being performed by hand!
Although for all this to be possible, there’s a critical piece of knowledge that needs to be present at
all times. From the Angular documentation:
Although interceptors can modify requests and responses, the HttpRequest and HttpResponse
instance properties are readonly, rendering them largely immutable. They are immutable for a
good reason: an app might retry a request several times before it succeeds, which means that the
interceptor chain can re-process the same request multiple times. If an interceptor could modify
the original request object, the re-tried operation would start from the modified request rather than
the original. Immutability ensures that interceptors see the same request for each try.
This information is vital to understand that we will always need to create a new request copy with
the intended changes to ensure a deterministic workflow. This will be helpful to understand why
the implementation always needs to call the clone method and then transform the request before
passing it through.
Yes! An app can have multiple interceptors, each dealing with its scope of action. For example, we
can have an interceptor dedicated to coping with auth, one dealing with error handling, a third
dealing with logging, etc. This is only possible because Angular has an intelligent way of
processing the requests. According to the Angular documentation:
Angular applies interceptors in the order that you provide them. For example, consider a situation
in which you want to handle the authentication of your HTTP requests and log them before
sending them to a server. To accomplish this task, you could provide an AuthInterceptor service
and then a LoggingInterceptor service. Outgoing requests would flow from the AuthInterceptor to
the LoggingInterceptor. Responses from these requests would flow in the other direction, from
LoggingInterceptor back to AuthInterceptor.
The following diagram could represent the use case described:
140
Although only the user configured the auth and logging interceptors, Angular has another
interceptor for default handling all back-end server calls. This interceptor is called HTTP backend
and is always the last on the execution chain, independently of how many other interceptors are
created and configured by the user.
It’s crucial to understand that once the order of execution of this chain is established, it cannot be
changed. You also cannot remove or disable an interceptor on the fly. From the
Angular documentation:
You cannot change the order or remove interceptors later. If you need to enable and disable an
interceptor dynamically, you’ll have to build that capability into the interceptor itself.
Why is it so essential to understand this? Because they will consistently execute the request in the
same order, they were configured. This could make a big difference when thinking about what
order should be used to avoid surprises when expecting a detailed response and receiving a
different one because the interceptor that executed it before in the chain already transformed it.
Now that we have a basic idea of an interceptor and its primary purpose, it’s time to talk about
implementation. How to implement an Angular HTTP interceptor? I will be showing a few
examples of the most common use cases, such as adding custom HTTP headers, caching,
logging, and error handling.
Initial Setup
Since the scope of this document is around HTTP interceptors, I am assuming the reader would
have a previously created Angular project.
Now create a new interceptor using Angular CLI and briefly discuss it on your existing project. As
mentioned before, an interceptor is nothing more than an Angular service that implements a
specific interface. Let’s run the following command: ng generate interceptor example.
This CLI command will create an interceptor called ExampleInterceptor with the following code:
ng generate interceptor example
1 import { Injectable } from '@angular/core';
2 import {
3 HttpRequest,
141
HttpHandler,
4
HttpEvent,
5
HttpInterceptor
6
} from '@angular/common/http';
7
import { Observable } from 'rxjs';
8
9
@Injectable()
10
export class ExampleInterceptor implements HttpInterceptor {
11
12
constructor() {}
13
14
intercept(request: HttpRequest<unknown>, next: HttpHandler):
15
Observable<HttpEvent<unknown>> {
16
return next.handle(request);
17
}
18
}
As we can see, an interceptor service implements the HttpInterceptor interface, imported from the
Angular common module. We need to implement the intercept method with our customized code
for each use case. This method receives an HTTP request performed by the app and the handler
executing the chain of calls. If no custom transformations are in place, it will simply pass it through
to the handle method (next.handle(request)) and repeat the same process on all the subsequent
configured interceptors (as explained in the workflow diagram above).
One of the most common use cases for interceptors is handling auth requests. This can be
achieved easily by adding the required headers to the outgoing request inside the intercept
method.
The examples provided are, of course, using mocked examples of security tokens. The main goal
is to show the reader how to implement auth interceptors by adding custom headers to the
requests. In this scenario, we will be adding an authorization token. The examples discussed
below should be easily adaptable to a real-world application with a proper authentication system.
If you are interested in learning more about authorization and building secure Web
applications consider joining our flagship online program Web Security Academy. It will teach you
everything you need to know in that area. You can also check our write-up covering secure
development training with actionable steps to build secure Web applications.
Basic Authentication
Consider a Basic Authentication scenario where we must authorize every request accessing the
API. We could have an interceptor like the following:
1 import { Injectable } from '@angular/core';
2 import {
3 HttpRequest,
4 HttpHandler,
5 HttpEvent,
6 HttpInterceptor
7 } from '@angular/common/http';
8 import { Store } from '@ngxs/store';
9 import { Observable } from 'rxjs';
142
10 import { AuthState } from '../../store/auth.state';
11
12 @Injectable()
13 export class AuthInterceptor implements HttpInterceptor {
14
15 constructor(private authService: AuthService) {}
16
17 intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
18 return next.handle(this.addAuthToken(request));
19 }
20
21 addAuthToken(request: HttpRequest<any>) {
22 const token = this.authService.getAuthToken();
23
24 return request.clone({
25 setHeaders: {
26 Authorization: `Basic ${token}`
27 }
28 })
29 }
30 }
143
JWT Authentication
The previous example was simple enough to explain how to create a trivial request transformation
inside the intercept function and create a new request with the new auth header.
Although apart from a small number of use cases, in the current days, ‘Basic Authentication’ is not
a common scenario for most apps.
One common usage of the auth interceptor would be handling requests dealing with a JWT token.
I will start by showing a typical implementation and then break it down into steps for more clarity.
Let’s consider a use case where we have an app with JWT authentication with refresh token
support:
1 import {
2 HttpErrorResponse,
3 HttpEvent,
4 HttpHandler,
5 HttpInterceptor,
6 HttpRequest,
7 } from '@angular/common/http';
8 import { Injectable } from '@angular/core';
9 import { BehaviorSubject, Observable, throwError } from 'rxjs';
10 import { catchError, filter, finalize, switchMap, take } from 'rxjs/operators';
11 import { AuthService } from './auth.service';
12
13 @Injectable()
14 export class JwtAuthService implements HttpInterceptor {
15 private refreshTokenInProgress = false;
16 private refreshTokenSubject = new BehaviorSubject(null);
17
18 constructor(private authService: AuthService) {}
19
20 intercept(
21 request: HttpRequest<any>,
22 next: HttpHandler
23 ): Observable<HttpEvent<any>> {
24 return next.handle(this.addAuthToken(request)).pipe(
25 catchError((requestError: HttpErrorResponse) => {
26 if (requestError && requestError.status === 401) {
27 if (this.refreshTokenInProgress) {
28 return this.refreshTokenSubject.pipe(
29 filter((result) => result),
30 take(1),
31 switchMap(() => next.handle(this.addAuthToken(request)))
32 );
33 } else {
34 this.refreshTokenInProgress = true;
144
35 this.refreshTokenSubject.next(null);
36
37 return this.authService.refreshAuthToken().pipe(
38 switchMap((token) => {
39 this.refreshTokenSubject.next(token);
40 return next.handle(this.addAuthToken(request));
41 }),
42 finalize(() => (this.refreshTokenInProgress = false))
43 );
44 }
45 } else {
46 return throwError(() => new Error(requestError.message));
47 }
48 })
49 );
50 }
51
52 addAuthToken(request: HttpRequest<any>) {
53 const token = this.authService.getAuthToken();
54
55 if (!token) {
56 return request;
57 }
58
59 return request.clone({
60 setHeaders: {
61 Authorization: `Bearer ${token}`,
62 },
63 });
64 }
65 }
This example is considerably more complex than the basic authentication one, but let’s break it
into small parts, and it will seem a lot less overwhelming.
1 private refreshTokenInProgress = false;
2 private refreshTokenSubject = new BehaviorSubject(null);
When working with JWT, it is standard to make usage of a refresh token. It’s one of the practices
used. In our interceptor, we will be using a Boolean variable to store a temporary state. At the
same time, a refresh token is being loaded and a Behaviour Subject to keep the state of the last
change. We are, of course, initializing it to null since when loading the application, until the user
authentication is completed successfully, a token will not be created.
1 return next.handle(this.addAuthToken(request)).pipe(
2 catchError((requestError: HttpErrorResponse) => {
This call of the intercept method is a bit different than the one on the minor example. When a user
tries to perform a request to an API or a view of an app to which it has not yet been authorized on
a properly designed product, will receive an exception with the HTTP error code 401. Knowing
this, the correct way to handle the workflow on the interceptor is to capture the exceptions and
process the request according to the type of exception triggered.
1 if (requestError && requestError.status === 401) {
2 if (this.refreshTokenInProgress) {
3 return this.refreshTokenSubject.pipe(
4 filter((result) => result),
145
5 take(1),
6 switchMap(() => next.handle(this.addAuthToken(request)))
7 );
8 }
If the user tries to access an API without the expected authorization will get an exception with
status code 401 (unauthorized). At this point, some additional checks need to be done to decide
how to proceed with the request. If we already have a new refresh token being processed, the
workflow will wait until the token is available and provided to the behavior subject. Once it is finally
available, we add the token to the header and pass the transformed request through.
1 else {
2 this.refreshTokenInProgress = true;
3 this.refreshTokenSubject.next(null);
4
5 return this.authService.refreshAuthToken().pipe(
6 switchMap((token) => {
7 this.refreshTokenSubject.next(token);
8 return next.handle(this.addAuthToken(request));
9 }),
10 finalize(() => (this.refreshTokenInProgress = false))
11 );
12 }
When no refresh token has been requested yet, a new process starts. We start by flagging the
application that a new refresh token request is now in place, and we ensure that no unexpected
value is pending on the behavior subject by setting it to null. This will ensure the request will wait
until a token is provided (as shown in the former example).
The only thing left to do is now request a new refresh token, emit it to the refresh token subject as
soon as it’s available and then add the token to the request header.
Finally, we need to ensure we leave the process clean, so we reset the refresh token flag to false.
1 else {
2 return throwError(() => new Error(requestError.message));
3}
For the scope of this example, if the error status code is not 401, we throw the error up to
potentially be caught by a dedicated error interceptor.
146
Learn more
Caching Interceptor
Caching is itself a considerably broad and complex subject. Some configurations and nuances can
significantly improve application performance or be the root of a significant number of problems
when poorly implemented.
Angular documentation provides a comprehensive example of how to implement a caching
interceptor. However, it can be overwhelming for the reader who is going through the first steps of
implementing an interceptor. This section will provide a simplified version to explain how it works.
Angular interceptors can handle HTTP requests themselves without passing them through to the
next request handler. We can take advantage of this feature by setting some requests in the cache
to improve performance and user experience by reducing the number of times needed to travel to
the server.
A simplified caching interceptor could be implemented as follows:
1 import {
2 HttpEvent,
3 HttpHandler,
4 HttpInterceptor,
5 HttpRequest,
6 HttpResponse,
7 } from '@angular/common/http';
8 import { Injectable } from '@angular/core';
9 import { Observable, of, tap } from 'rxjs';
10
11 @Injectable()
12 export class CachingInterceptor implements HttpInterceptor {
13 private cache = new Map<string, any>();
14
15 intercept(
16 request: HttpRequest<any>,
17 next: HttpHandler
18 ): Observable<HttpEvent<any>> {
19 if (request.method !== 'GET') {
20 return next.handle(request);
21 }
22 const cachedResponse = this.cache.get(request.url);
23 if (cachedResponse) {
24 return of(cachedResponse);
25 }
26
27 return next.handle(request).pipe(
28 tap((response) => {
29 if (response instanceof HttpResponse) {
30 this.cache.set(request.url, response);
31 }
32 })
33 );
34 }
35 }
Our cache is defined by a Map structure that will store a key-value pair. In our simplistic example,
the cache will store a URL as the key and result of the response call to the sample API. We are
only caching ‘GET’ requests since these are idempotent. This means that for the exact same
input, no matter how many times a request is made, it should produce the same output.
147
1 const cachedResponse = this.cache.get(request.url);
2 if (cachedResponse) {
3 return of(cachedResponse);
4}
We start by getting an existing cached response for the requested URL. When we have a cache
hit, we return the response previously stored.
1 return next.handle(request).pipe(
2 tap((response) => {
3 if (response instanceof HttpResponse) {
4 this.cache.set(request.url, response);
5 }
6 })
7 );
We pass the request through to the server on a cache miss and store the response in the cache.
This is possible because, as explained initially, interceptors can handle both HTTP requests and
HTTP responses together.
A fully working example can be found here. The first time the button ‘Cached Request’ is clicked, it
will perform a request to the API. This can be verified on the developer tools network panel. Any
subsequent clicks on the button will not trigger any additional requests. This can be tested by
clicking the ‘Clear Data’ button and then clicking the ‘Cached Request’ button again. Although the
displayed data gets cleared and shows again, no new requests are made to the server API. After
the first request, all the others will return from the cache.
Logging Interceptor
Modern-day applications usually provide a significant number of features to the end-users. The
more complex these applications are, the more error-prone they can become. Collecting
meaningful data from an entire HTTP operation or specific properties from user data will allow
insightful and dynamic thinking on producing some valuable statistics. These can measure the
average request elapsed time for detecting potential bottlenecks or logging requests input data for
detecting malformed requests that are triggering unexpected responses. There are hundreds of
other valuable scenarios where logging could be helpful.
For this example, we will use the implementation provided on Angular documentation and break it
down:
1 import {
2 HttpHandler,
3 HttpInterceptor,
4 HttpRequest,
5 HttpResponse,
6 } from '@angular/common/http';
7 import { Injectable } from '@angular/core';
8 import { finalize, tap } from 'rxjs';
9 import { MessageService } from './message.service';
10
11 @Injectable()
12 export class LoggingInterceptor implements HttpInterceptor {
13 constructor(private messageService: MessageService) {}
14
15 intercept(req: HttpRequest<any>, next: HttpHandler) {
16 const started = Date.now();
17 let ok: string;
18
19 return next.handle(req).pipe(
20 tap({
148
21 next: (event) =>
22 (ok = event instanceof HttpResponse ? 'succeeded' : ''),
23 error: (error) => (ok = 'failed'),
24 }),
25
26 finalize(() => {
27 const elapsed = Date.now() - started;
28 const msg = `${req.method} "${req.urlWithParams}"
29 ${ok} in ${elapsed} ms.`;
30 this.messageService.add(msg);
31 })
32 );
33 }
34 }
1 const started = Date.now();
2 let ok: string;
We start by defining the moment the request was intercepted and a variable to store the outcome
of the backend server response.
1 tap({
2 next: (event) =>
3 (ok = event instanceof HttpResponse ? 'succeeded' : ''),
4 error: (error) => (ok = 'failed'),
5 }),
Since interceptors can handle both an outgoing request and an incoming response, let’s store the
outcome in our previously defined variable. Depending on the backend server returning a success
or an error, the variable will print either ‘succeeded’ or ‘failed’.
1 finalize(() => {
2 const elapsed = Date.now() - started;
3 const msg = `${req.method} "${req.urlWithParams}"
4 ${ok} in ${elapsed} ms.`;
5 this.messageService.add(msg);
6 })
The finalize method always executes, independently of the observable returning a success or an
error response. This will then be the perfect place to calculate how long the request took in total
and, for this example, to create a message with the elapsed time and the response status.
A fully working example can be found here. By clicking the ‘Cached Request’ button, it will log in to
the console the time elapsed for the request and the status.
The curious reader will attempt to click the button several times, but no more logs will be displayed
on the console. Why is this happening? As a hint, try to look at the ‘app.module’ file and see how
the interceptors are declared and in which order. Does the order make any difference? Try placing
the logging interceptor before the caching interceptor and observe the results.
Errors from an API response to an HTTP call are never desirable for any application.
Nevertheless, the best way of dealing with them is to assume they can (and will happen) and
provide an elegant way to handle them. Failed requests can happen for many reasons, and the
last thing an end-user would want to receive is a broken view or a considerable number of errors
being displayed.
An elegant solution can be implemented by creating an error handler to intercept all HTTP errors.
1 import {
2 HttpEvent,
3 HttpHandler,
4 HttpInterceptor,
149
5 HttpRequest,
6 } from '@angular/common/http';
7 import { Injectable } from '@angular/core';
8 import { MessageService } from 'primeng/api';
9 import { Observable, throwError } from 'rxjs';
10 import { catchError } from 'rxjs/operators';
11
12 @Injectable()
13 export class ErrorInterceptor implements HttpInterceptor {
14 constructor(private messageService: MessageService) {}
15
16 intercept(
17 request: HttpRequest<any>,
18 next: HttpHandler
19 ): Observable<HttpEvent<any>> {
20 return next.handle(request).pipe(
21 catchError((requestError) => {
22 if (requestError.status !== 401) {
23 const { error } = requestError;
24 this.messageService.add({
25 severity: 'error',
26 summary: `HTTP Error - ${requestError.status}`,
27 detail: error && error.message,
28 });
29 }
30 return throwError(() => new Error(requestError));
31 })
32 );
33 }
34 }
There’s not much explanation needed since the code should be self-explanatory. The only detail
that is important to discuss is filtering the errors. We are only dealing with errors in which the
HTTP response is different from 401.
Why is this? At the beginning of this article, I mentioned that it is possible to have multiple
interceptors, executing them in a chained process. Since we already have the auth interceptor
coping with all the 401 errors and processing those requests, there’s no point in managing them
on this interceptor also.
When an error is received, this example merely displays a toast with the error message to the
user, but this would be the perfect place to format or create customized notifications based on
specific errors.
Conclusion
Angular 4.3 simplified everyone’s lives by adding the HTTP interceptors to the HTTP client library.
The ability to handle outgoing requests and incoming responses in the same place together
opened many possibilities such as the ones discussed and demonstrated above.
The examples provided are only the tip of the iceberg of all possibilities. The main goal was to
show the reader how to implement and cope with the most standard use cases around any
modern-day application.
The critical concepts are that interceptors can handle incoming and outgoing requests, can
transform them by creating a new mutated request, and can themselves return without passing the
request to the next interceptor. The latter should probably be used sparingly, but it’s essential to
know it can be done.
In this article
150
1. What is an Angular HTTP Interceptor
2. How to Create HTTP Interceptor
3. 10 Ways to Use Interceptors in Angular
4. Create the Interceptor
5. Setting the New Headers
6. Cancel the current Request
7. Change the Requested URL
8. Conclusion
9. Frequently Asked Questions (FAQs)
View All
An application is incomplete without the backend server that displays data on UI. Various
backend servers are available on the internet. Most application uses JSON and XML servers to
perform their tasks. The API is used for the backend communication between components and
methods. This communication is based on two significant concepts: authorization and
authentication.
Interceptors are another significant part of Angular programming. They are used to modify the
HTTP requests by adding several functionalities.
Interceptor in angular is a great way to modify your code and make it more readable. Also, they
are used in various other fields, which we will discuss later.
For more information, check out the angular js certification course and become an
advanced angular developer in no time.
The angular interceptor is a medium connecting the backend and front-end applications.
Whenever a request is made, the interceptors handle it in between. They can also identify the
response by performing Rxjs operators.
The interceptors do not initiate the handle method and handle the requests at their level. The
interceptor is used to perform various functions and methods to perform specific tasks. We
would highly recommend you to refer to the best web development course online course and
get your hands on angular.
151
How to Create HTTP Interceptor
Before implementing an angular interceptor, you must create one. So, here we will help you
create a service that will implement the HTTP interceptor angular interface. Here are the code
snippets and examples to create an interceptor of your own.
Then add the class within the root module. Use the token named HTTP_INTERCEPTOR.
There are various ways to use the angular HTTP interceptor. We have selected the top 10 most
straightforward ways to use the angular HTTP interceptor. We will study them with relevant
codes and examples.
1. Loaders
Interceptors can be used as loaders whenever there exist different active requests. A loader
function with both, hide and show features is used to handle the requests.
2. URL
Changing the URLs is what the interceptor angular is capable of. You can change the URL, and
the interceptor will behave like an API interceptor used to add prefixes.
3. Headers
152
Angular interceptors manipulate the headers as they provide features such as authentication
and authorization. The angular HTTP interceptors are used to protect the application against
XSRF.
4. Converting
Interceptor can even convert the format of an API that we receive. A more likely example is to
convert the XML file to a JSON file.
5. Errors
Interceptors are used in two ways to detect the errors- retry the HTTP call, and the second one
is to keep a check on the status of the exception that occurred.
There are various HTTP error codes such as error codes 400, 500, and 200 and their series,
which are to be handled individually. The error code 500 determines that the API has not
received the request. The error code 401 means that the access token is no longer active.
6. Notifications
Interceptors can even show various messages as notifications. In the below example, we have
printed an “object created” message on the console. The notifications can also be displayed
whenever we detect an error.
7. Fake backend
153
As the name suggests, the fake backend is used to develop the application when the backend
doesn’t exist. The response is based on the request, after which we return an observable object
of the HTTP request.
8. Profiling
Interceptors have the ability to both requests and respond simultaneously. They can even log
the HTTP operation without any time delay. So, it becomes easy to note the time of both the
request and the response and can be consoled together.
Different profiles can be logged into the database to get the information.
9. Authentication
Authentication is the basic functionality added to any application to check the user’s
authentication. This is a common and basic use of the interceptors to make a check-in at the
application’s usage. It connects various parameters such as the refresh tokens, adds bearer
tokens, and redirects to the login URL.
154
10. Catching
Interceptors are capable enough to detect any errors as it handles the requests besides
forwarding them to the handle method. We will use a key-value map where the key will be more
like a URL. The observable response can be returned directly to the handle function as soon as
we find any relevant response on the map.
This is feasible as it saves a lot of time and energy. The developers do not always need to
search everything in the backend when they have already caught one.
To create an interceptor angular, create a file named AppHTTPInterceptor.ts within the src-app
folder. Refer to the below code or place it just the same way.
Import all the essential modules required by the application, such as the observable,
injectable, HTTP requests, HTTP handler, HTTP events, etc.
Now, create a class implementing the HTTP interceptor in angular interface.
155
Later, you need to create one interceptor method with two arguments. The arguments
would be the HTTP request and the HTTP handler.
In the method's body, you can change the code at your convenience. After making the
changes, just call the handle method of the HTTP handle using the HTTP request object.
The handle method initiates the upcoming interceptor or sends the HTTP request
straightaway to the server.
Setting the New Headers
We have already consoled the request. This section will add HTTP and custom headers to the
request. So, let us learn how to do it,
1. Add the content-type: Modifying request needs a clone of the request on which we will be
performing the task, the request. The clone method helps us to modify the request and its
properties. The header object is immutable, so we need to clone the request.
RequestAppendAppend is an alternative to the clone method. If not, then use the shortcut
named set header.
2. Add the authorization token: Add the authorization token in your code as given below.
Cancel the current Request
Angular has a very convenient way to cancel the requests coming from the browser. Simply
returning an empty object would cancel the request in no time. We have an example below
where we are checking the login activity of a user.
If the user is logged in, then the request continues, and if not, the request is canceled. Follow
the below code and make changes wherever necessary.
Angular provides a way to change a URL before it reaches the server. The URL property is
enclosed within the HTTP request, and you can change it as per your requirement.
156
If you want to add a base URL for all your HTTP requests, just replace HTTP with HTTPS, and
your task is done. Here we have an example of how you can do it. Refer to the below code and
follow the same.
Looking to level up your coding skills? Discover the power of Python with our unique online
course. Learn Python online and unlock endless possibilities. Join now!
Conclusion
Hopefully, we could demonstrate all the possible facts and things related to the interceptors
angular. Interceptors are a great way to make your code scalable to an extent. You can perform
various actions just by creating HTTP interceptors in angular.
So, if you want to study more about the angular js, you must check our
course- KnowledgeHut’s angular js certification course.
157
3. How do you use an interceptor in angular 11?
Ans. The HTTP interceptor angular has the latest module added to the list. The HTTP client
module modifies the HTTP requests before sending them to the back ends.
4. Can we have more than one interceptor in angular?
Ans. Yes, you can have multiple angular interceptors by changing the value of multi to true.
This will inform the classes regarding the usage of the angular HTTP interceptors.
5. Does the order of interceptors matter?
Ans. Interceptors have a pre-defined order determined by the order in which they are declared.
And, by default, they follow the same order, and so do the developers.
6. What is HTTPBackend in angular?
Ans. The HTTPBackend is a service used in angular to model all the HTTP requests made by
the application. It mentions the approximate requests and values to return for a specific request.
==
158