We have looked at the concept of reactive x programming, Observables, Subjects and some operators in Rxjs. We will now look at using what we have learnt so far in our code to do common things we do without Observables normally.
Making Http Request
To make an Http Request with Observables we can import the ajax operator from the library, we will also import the map operator and the catchError operator. The map operator will allow us to map the value we receive from the request to a particular value or action, while the catchError operator will help us handle errors that arises from the request.
import { ajax } from 'rxjs/ajax'
import { map, catchError } from 'rxjs/operators'
const Users$ = ajax.getJSON('https://jsonplaceholder.typicode.com/users')
// the getJSON method on the ajax operator is used to fecth json data
Users$.pipe(
map(users => console.log(users),
catchError(err => console.log(err))
)
Users$.subscribe()
// logs out a list of users from json placeholder
Post Requests
This is simpler, easier to read and maintain. We can also make post and get requests and maybe return an Observable from the error, so we can deal with in in an observer?
import { ajax } from 'rxjs/ajax'
import { of } from 'rxjs'
import { map, catchError } from 'rxjs/operators'
const Users$ = ajax({
url: 'the-api-we-are-sending-the-data-to',
method: 'POST', // can be PUT, PATCH, DELETE
headers: {'Content-Type': 'application/json'},
body: { name: 'superman'}
})
Users$.pipe(
map(data => console.log(data)),
catchError(err => return of(err))
)
Users.subscribe(v => console.log(v))
// should log out response from server if everything went well
// if there is an error the observer will subscribe to it and log it out too
Combining Two Observables
We can use the mergeMap operator to combine values we get from two Observables into a single value, this might be useful if we make a post request to add a new user to the database and the API returns some data about the new user we just created for us. We can use information on that returned user to make another request to the API to retrieve some other data based on a property on the user.
import { ajax } from 'rxjs/ajax'
import { of, zip } from 'rxjs'
import { mergeMap, catchError } from 'rxjs/operators'
const User$ = ajax({
url: 'url-to-server',
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: { hero: 'superman'}
})
User$.pipe(
mergeMap(hero => ajax.getJSON('url-to-get-another-resource'+hero.id)),
catchError(err => of(err))
)
User$.subscribe()
Zip
We can also use the zip operator to combine two Observables of the same length into a single array. This is particularly useful if we make a request to two different API that return equal length of items.
import { ajax } from 'rxjs/ajax'
import { zip, of } from 'rxjs'
import { catchError, map } from 'rxjs/operators'
const marvelHeroes$ = ajax.getJSON('url-to-api').pipe(
map(heroes => hero.name),
catchError(err => of(err))
);
const dcHeroes$ = ajax.getJSON('url-to-api').pipe(
map(heroes => hero.name),
catchError(err => of(err))
)
zip(marvelHeroes$, dcHeroes$).subscribe(x => console.log(x))
ConcatAll
We can use this operator to flatten out higher order Observables, i.e Observable of Observables, the concatAll operator subscribes to incoming Observables emitted by the higher order observable and emits the value it receives from each inner Observable.
import { ajax } from 'rxjs/ajax'
import { of, from } from 'rxjs'
import { map, catchError, concatAll } from 'rxjs/operators'
const users$ = from([1, 2, 3, 4, 5])
users$.pipe(
map(id => ajax.getJSON('url-to-api'))
concatAll(),
catchError(err => of(err))
)
users$.subscribe(data => console.log(data))
Mapping more than value on an object
We can use the switchMap operator to return more than one data on the data we receive from the API, this operator is particularly useful when we want only some properties on the data we are getting from the API.
import { ajax } from 'rxjs/ajax'
import { of } from 'rxjs'
import { switchMap, catchError } from 'rxjs/operators'
const User$ = ajax.getJSON('url-to-api')
User$.pipe(
switchMap(user => of(user.name, user.id, user.id)),
catchError(err => of(err))
)
User$.subscribe(x => console.log(x))
Filtering data
We can use the filter() operator to filter the response from the server to ensure that we only get secluded data back from the response. Say we have an API that returns a list of orders, we can filter the orders and return only pending or fulfilled orders.
import { ajax } from 'rxjs/ajax'
import { of } from 'rxjs'
import { filter, catchError, map } from 'rxjs/operators'
const Orders$ = ajax.getJSON('url-to-orders-api')
Orders$.pipe(
map(orders => orders),
filter(order => order.status === 'pending'),
catchError(err => of(err))
)
Orders$.subscribe(data => console.log(data))
Taking only a part
We might make a request to an API that returns a list of items but we are only interested in the most recent data, we can use the take operator to limit the amount values we want. Let's see a typical use case.
import { ajax } from 'rxjs/ajax'
import { of, from } from 'rxjs'
import { map, take, catchError, concatAll } from 'rxjs/operators'
const users$ = from([1, 2, 3, 4])
users$.pipe(
map(id => ajax('url'+id)),
concatAll(),
take(2),
catchError(err => of(err))
)
users$.subscribe(x => console.log(x))
The concatAll() operators listens to incoming Observables emitted by that from the map function and subscribes to it providing us with the values emitted by each observable.
Time based Operation
We can also use the time based operators that Rxjs comes baked with to make our work easier. Let's say we have an API that returns a list of posts and we want to be fetching the latest posts from the database every say 10 seconds? we can use the interval operator to achieve this.
import { ajax } from 'rxjs/ajax'
import { of, interval } from 'rxjs'
import { map, take, catchError, concatAll } from 'rxjs/operators'
const posts$ = interval(10000)
posts$.pipe(
map(x => ajax.getJSON('url-to-post-api')),
concatAll(),
take(10),
catchError(err => of(err))
)
posts$.subscribe(post => console.log(post))
takeUntil
The above function will keep running in the background forever and performance wise this is not so smart, we can use the takeUntil operator to allow the Observable emit data for only 30secs.
import { ajax } from 'rxjs/ajax'
import { of, interval, timer } from 'rxjs'
import { map, takeUntil, take, catchError, concatAll } from 'rxjs/operators'
const psot$ = interval(10000)
post$.pipe(
takeUntill(timer(30000)),
map(x => ajax.getJSON('url-to-post-api')),
concatAll(),
take(10),
catchError(err => of(err))
)
posts$.subscribe(post => console.log(post))
Hope you find this useful