We have familiarized ourselves with the concept of Observables. We have also looked at Subjects. Now we will focus on the different operators Rxjs comes baked with. Those operators makes working with Observables light work, and if you don't find something that get the job done for you, you can add your own operators.
Operators
An operator is just a function, there are two types of operators;
- pipeable operators
- creation operators
Pipeable Operators
A pipeable operator is an operator that is called within the pipe()
function.
import { of } from 'rxjs'
import { filter } from 'rxjs/operators'
const evenNumbers$ = of([0,1,2,3,4,5,6,7,8,9,])
evenNumbers$.pipe(filter(num => num%2 !== 1));
evenNumbers$.subscribe(num => console.log(num))
// 0, 2, 4, 6, 8
We used a filter operator to filter out odd numbers, and later subscribed to the Observable. The filter operator can be called inside a pipe() function, that is why they say it is a pipeable operator.
Pipeable operators return an observable. Once we subscribe to the piped operator, the also subcribe to the Observable, and we get the same insance of the observable we subscribed to.
Pipeable operators do not create Observables, they only return it. We can call the operator directly on the Observable but in practice we will use quite a number of operators, calling it on the observable is going to makenour code cluncky. So you don't Observable.filter()
You do Observable.pipe(filter())
even if it's one, when we start using multiple operators you will understand why.
Creation Operators
These are standalone functions, they return a new instance of an observable when we use them so we don't bother about piping them.
import { from } from 'rxjs'
import { map } from 'rxjs/operators'
const numbers$ = from([0,1,2,3,4,5,6,7,8,9,])
numbers$.pipe(map(num => console.log(num));
numbers$.subscribe()
// 012356789
We have so many operators so they are categorized, we have
Creation Operators which we just discussed above. All other categories of observables are pipeable operators
Transfomation Operators which we can use to modify/transfom the values emitted by the Observable
Filtering Operators to sort out data.
Join operators to help us combine higher order observables, observable of observation.
join creation operators
Conditional Operators
Boolean Operators
Multicasting Operators
Error Handling Operators
utility Operators
Mathemical Operators
Transformation Operators
These are functions that allow us to make some modifications to our data.
Map
Take for instance, the map()
function, it works similar to the array map method. There are so many of the but I'll demo a few i use.
import { of, fromEvent } from 'rxjs'
import { map } from 'rxjs/operators'
const nums$ = of(1,2,3,4)
nums$.pipe(map(x => x + 2))
nums$.subscribe (x => console.log(x))
// 3, 4, 5, 6
// we can map to object properties
const clicks$ = fromEvent(document.body, 'click')
clicks$.pipe(map(e => e.target))
cliks$.subscribe(x => console.log(x))
MapTo
This is similar to map but it maps all values emitted to the same value.
import { from } from 'rxjs'
import { mapTo } from 'rxjs/operators'
const nums$ = from([2, 3, 4, 5, 6, 7, 8])
nums$.pipe(mapTo(x => 0))
nums$.subscribe(console.log(x))
// 0, 0,0,0,0,0,0
MergeMap
This function will create a map of two observables, it takes a function that we can return another Observable in. MergeMap will listen to both Observables and create a map for them.
import { from, of } from 'rxjs'
import { mergeMap } from 'rxjs/operators'
const nums$ = from([2, 3, 4])
nums$.pipe(mergeMap(x => of(`${x} value recieved`))
nums$.subscribe(console.log(x))
// 2 value recieved
// 3 value recieved
// 4 value recieved
This operator is particularly useful when we want to make one observable wait for another one.
mergeMapTo
This operator is similar to the mergeTo operator and the mergeMap operator, it accepts a function that returns an Observable that all values emitted from an earlier Observable, the values a mapped to a particular value.
import { from, of } from 'rxjs'
import { mergeMapTo } from 'rxjs/operators'
const nums$ = from([2, 3, 4])
nums$.pipe(mergeMapTo(x => of(`a value recieved`))
nums$.subscribe(console.log(x))
// a value recieved
// a value recieved
// a value recieved
switchMap
This operator is similar to mergeMap, but instead of mapping only once, we can return a list of maps we want. It can emit an inner Observable that accomplishes this.
import { from, of } from 'rxjs'
import { switchMap } from 'rxjs/operators'
const nums$ = from([2, 3, 4])
nums$.pipe(switcMap(x => x - 2, x + 2, x -* 2))
nums$.subscribe(console.log(x))
// 0
// 1
// 2
// 4
// 5
// 6
// 4
// 6
// 8
This operator is useful when we want to map more than one value on the input.
Filteration Operators
These set of operators filters our data, giving us the exact matches we need
filter
This works similar to the array.filter() method, it filters the value emitted by an Observerable based on the logic jn the function we pass to it.
import { from } from 'rxjs'
import { filter } from 'rxjs/operators'
const nums$ = from([-1, 0, 1, 2, 3, 4])
nums$.pipe(
filter(x => x > 1)
)
nums$.subscribe(x => console.log(x))
debounce
We can use the debounce operator when we want to delay the emission of values from an Observable.
import { fromEvent, interval } from 'rxjs';
import { debounce } from 'rxjs/operators';
const clicks$ = fromEvent(document, 'mouseOver');
const result$ = clicks$.pipe(debounce(() => interval(1000)));
result$.subscribe(x => console.log(x));
The above function will only emit the values every 1 second after we hover over the element. This function can be useful for maling search as you type feature, or a carousel.
Trottle
We can also trottle an Observable, that is emit a value first, then wait some time before emitting another value from the source observable. It is similar to debouce, the main difference is that trottle will emit a value amd then wait for our specified time before emitting the next, it will skip other values emitted by the source observable for that interval period. Debounce will wait the time given before it emits the first value aand other valuea emited by the source observable follows suit
import { from, interval } from 'rxjs';
import { trottle } from 'rxjs/operators';
const source$ = from([1,2,3,4, 5, 6, 7])
const result$ = source$.pipe(trottle(() => interval(1000)))
result.subscribe(x => console.log(x))
first
This operator only takes the first value emitted by a source observable.
import { from } from 'rxjs';
import { first } from 'rxjs/operators';
const source$ = from([1,2,3,4]);
const result$ = source$.pipe(first());
result$.subscribe(x => console.log(x));
find
We can also use a find operator, much like we use the array.find() method and it will return the value emitted from a source observable that meets our target.
import { fromEvent } from 'rxjs';
import { find } from 'rxjs/operators';
const source$ = fromEvent(document, 'click');
// return only the element whose id is para
const result$ = source$.pipe(find(ev => ev.target.id === 'para'));
result$.subscribe(x => console.log(x));
take
This operator is used to trim down the number of values we recieve from a source observable, if we pass in 2 as an argument to it, it will only take the first 2 values emitted by the source observable
import { interval } from 'rxjs'
import { take } from 'rxjs/operators'
const interval$ = interval(1000)
const firstFive$ = interval$.pipe(take(5))
firstFive$.subscribe(x => console.log(x))
// output
// 0
// 1
// 2
// 3
// 4
takeUntil
This operator is used to receieve values emitted by a source observable until a condition which is returned by another observable the takeUntil operator watches is true.
import { fromEvent, interval } from 'rxjs'
import { takeUntil } from 'rxjs/operators'
const source$ = interval(1000)
const clicks$ = fromEvent(document, 'click');
// take value emitted by the interval untill we click on the page
const result$ = source$.pipe(takeUntil(clicks$))
result$.subscribe(x => console.log(x))
You can head to the rxjs website to learn more about these operators. I will move into using this operators to solve real problems we might encounter in our code, as we use any operator, i'll discuss about it. Hope you find this useful