Update Docs - Observables

This commit is contained in:
francesco 2021-12-15 12:27:18 +01:00
parent 2232f4247f
commit 2972c596d9
4 changed files with 235 additions and 0 deletions

View File

@ -0,0 +1,235 @@
# 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.
<img src="../img/observable.png" alt="component" width="800"/>
It can also used promises or callbacks to achieve the same result. Observable is just an other approach.
## Install RxJS
```bash
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.
```ts
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
- ...
```ts
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.
```ts
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)
```ts
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.
<img src="../img/observable_operator.png" alt="component" width="800"/>
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.
<img src="../img/rxjs_subject.png" alt="component" width="800"/>
> Subject is the recommended way to emit events.
### Subject Example
user.service.ts
```ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({providedIn: 'root'})
export class UserService {
activatedEmitter = new Subject<boolean>();
}
```
app.component.ts
```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
```html
...
<p *ngIf="userActivated">Activated!</p>
...
```
user.component.ts
```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
```html
<button class="btn btn-primary" (click)="onActivate()">Activate</button>
```
## Ressource
[RxJS](https://rxjs-dev.firebaseapp.com/)

BIN
img/observable.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

BIN
img/observable_operator.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
img/rxjs_subject.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB