5.6 KiB
Observables
Observable emmits an event driven by the data source.
Observer subscribe to the observable to be notified when an event is emitted.
This is an asynchronous operation.
It can also used promises or callbacks to achieve the same result. Observable is just an other approach.
Install RxJS
npm install --save rxjs@6
npm install --save rxjs-compat
Example and general usage
In this case params
is an observable which we can subscribe to.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
id: number;
constructor(private route: ActivatedRoute) {
}
ngOnInit() {
this.route.params.subscribe((params: Params) => {
this.id = +params.id;
});
}
}
It exists various types of observable.
- executed once
- executed periodically
- ...
import { interval } from 'rxjs';
interval(1000).subscribe( count => {
consol.log(count);
})
interval
is a built-in observable from rxjs.
If the app don't unsubscribe
from the observable can cause memory leaks. Also, if we navigate to an other route, countless observable will be created and couses performance issues.
When we leave the component we can call the OnDestroy
function to unsubscribe from the observable.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { interval, Subscription, Observable } from 'rxjs';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit, OnDestroy {
private firstObsSubscription: Subscription;
constructor() {}
ngOnInit() {
this.firstObsSubscription = interval(1000).subscribe(count => {
console.log(count);
});
ngOnDestroy(): void {
this.firstObsSubscription.unsubscribe();
}
}
In this example, the first subscription is stored in firstObsSubscription
variable, which at leaving the component will be destroyed by calling unsubscribe()
.
Note: For all Angular built-in observables, is not necessary to unsubscribe from the observable. This is handleb by Angular itself.
Building an observable (data, error, complete)
const customIntervalObservable = Observable.create(observer => {
let count = 0;
setInterval(() => {
observer.next(count);
if (count === 5) {
observer.complete();
}
if (count > 3) {
observer.error(new Error('Count is greater 3!'));
}
count++;
}, 1000);
});
this.firstObsSubscription = customIntervalObservable.subscribe(data => {
console.log(data);
}, error => {
console.log(error);
alert(error.message);
}, () => {
console.log('Completed!');
});
The subscribe
function accepts up to three arguments: data
, error
and complete
.
Note: if the observable is completed, is not necessary to
unsubscribe
.
Note: If an error is thrown, the observable is cancelled but not completed.
Operators
Operators are grate to transform our data. The application can then subscribe to the result of the operator. On the operator can be called the pipe()
function. Every operator has such a method. This function depends on the rxjs/operators
pakage.
This is usefull when the application is fetching data from the server and it should trandformed before the component will use it.
Subjec
Subject from RxJS is a more active then the event emitter because it can be called the function next()
from the outside.
Subject is the recommended way to emit events.
Subject Example
user.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({providedIn: 'root'})
export class UserService {
activatedEmitter = new Subject<boolean>();
}
app.component.ts
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { UserService } from './user.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
userActivated = false;
private activatedSub: Subscription;
constructor(private userService: UserService) {
}
ngOnInit() {
this.activatedSub = this.userService.activatedEmitter.subscribe(didActivate => {
this.userActivated = didActivate;
});
}
ngOnDestroy(): void {
this.activatedSub.unsubscribe();
}
}
app.component.html
...
<p *ngIf="userActivated">Activated!</p>
...
user.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { UserService } from '../user.service';
@Component({
selector: 'app-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
id: number;
constructor(private route: ActivatedRoute, private userService: UserService) {
}
ngOnInit() {
this.route.params.subscribe((params: Params) => {
this.id = +params.id;
});
}
onActivate() {
this.userService.activatedEmitter.next(true);
}
}
user.component.html
<button class="btn btn-primary" (click)="onActivate()">Activate</button>