Introduction to Angular 7 Observables
Hi folks,
As promised in the form tuts, today we talk about Observables!
This part is very important for Angular 7 and also the future of JavaScript in general.
Observables are elements that will give us information. In order to get access to those information, we need to subscribe to them (a bit like Netflix, but free and without the movies ...)
They offer a very dynamic use because they are :
- Compositional: You can use Combinators in order to filter or map for example.
- Lazy: They only start working when an Observer starts listening
We will have a look at three cases:
- Creating an Observable and coupling it with its Observer
- Handling the Http Service's Observable
- Subscribing to a pseudo infinite flow of data
When an Observable meets an Observer
The simplest case to understand how an Observable flow works is to create one.
Let's start by the imports:
import { Component } from '@angular/core';
import { Observable, Observer, Subscription, fromEvent } from 'rxjs';
import { map } from 'rxjs/operators';
As you can see, we also grab the map operator which will allow us to go through our data in the second case.
The best practice is to grab one element at a time and not the whole library.
Now let's get right into the subject!
export class AppComponent {
observable: Observable<string>;
observer: Observer<string>;
subscription: Subscription;
ngOnInit() {
this.observable = new Observable((observer: Observer<string>) => {
this.observer = observer;
});
}
}
In our AppComponent we create our homemade Observable and set our Observer.
Then we subscribe to our Observable:
ngOnInit() {
this.observable = new Observable((observer: Observer<string>) => {
this.observer = observer;
});
this.observable
.subscribe(this.handleData, this.handleError , this.handleComplete);
this.observer.next('12');
this.observer.next('15');
this.observer.complete();
this.observer.next('16');
}
The subscribe method allows us to handle three cases:
- When it's all good and there are data (we will call a method named handleData)
- When it's very bad and there is an error (we will call a method named handleError)
- When the flow gets closed (we will call a method named handleComplete)
Once it's done, we can use our observable to create a flow.
The flow will be: send 12 - send 15 - close the gate | send 16.
Since we close the gate, we will never receive the value 16.
And here the (very simple) methods that we will use to show the data:
export class AppComponent {
.
.
.
handleData(data) {
console.log('Here are the usable data', data);
// Insert Business logic here
}
handleComplete() {
console.log('Complete');
}
handleError(error) {
console.log('error:', error)
return Observable.throw(error);
}
.
.
.
}
Http's Observable
One of the most important part of programming is making requests to external sources.
Angular 7 Http Service now returns an Observable by default instead of a Promise.
Let's see how we can handle this.
First adding the HttpModule in the app.module.ts:
import { HttpModule } from '@angular/http';
@NgModule({
imports: [
.
.
.
HttpModule
]
.
.
.
})
export class AppModule {}
Then importing the Http Service:
import { Http } from '@angular/http';
export class AppComponent {
constructor(private http: Http) { }
}
Now we make a request to a cool API service:
ngOnInit() {
this.http.get('https://jsonplaceholder.typicode.com/posts')
.pipe(map(this.extractData))
.subscribe(this.handleData, this.handleError, this.handleComplete);
}
The only part that changes here is that we need to format the data that we get from the network. We will use a method called extractData for this job.
extractData(response) {
console.log('Raw response:', response);
console.log('Formatted response:', response.json());
const body = response.json();
return body || { };
}
There is a cool .json() method that helps us here.
The raw data:
And the formatted data:
So why use Observables when we can just keep the good old Promises?
Observables are way more powerful, tools like .retry allow us to make x amount of requests in case the network is unstable, we can also cancel requests and so much more! (http://reactivex.io/rxjs/)
Still don't want to use them?
You can add .toPromise (that you will need to import too) to return a Promise instead.
Pseudo Infinite flow
The previous example was a finite stream.
Let's subscribe to a pseudo infinite stream: the mouse.
ngOnInit() {
this.subscription = Observable.fromEvent(document, 'mousemove')
.subscribe(this.handleData, this.handleError, this.handleComplete);
}
Very simple here, we subscribe to the document and only ask for the 'mousemove' events.
We stock the subscription into a variable for later.
Every time that we move the mouse now we will have the information:
Unlike before, we need to clean up this time!
In the previous case, Http handled this like a boss.
Now we use our variable in the OnDestroy hook to unsubscribe otherwise we will have a memory leak in our application.
ngOnDestroy() {
this.subscription.unsubscribe();
}
Conclusion
Observables are great.
It can be a bit awkward to move from Promises since we are not used to all those variables, imports and operators.
Basically it can be summed up to using Combinators like map, flatMap, retry then subscribing to the result and finally if the flow is infinite we unsubscribe in order to avoid creating a Memory Leak.