Angular: Observable, Subject,
Observable – это особый объект, на который можно подписываться и мониторить его изменения в реальном времени.
Документация, но в нее углубляться нет смысла, если работать только с ангуляром.
Пример Observable
Простой, где мы просто получим в консоли значения 1, 2, 3… с интервалом в секунду. Здесь только подписка на него (subscribe), так как в остальных двух типах обработки нет смысла.
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/Rx'; import { Observer } from 'rxjs/Observer'; import { Subscription } from 'rxjs/Subscription'; export class HomeComponent implements OnInit, OnDestroy { numbersObsSubscription: Subscription; constructor() { } ngOnInit() { const myNumbers = Observable.interval(1000); this.numbersObsSubscription = myNumbers.subscribe( (number: number) => { console.log(number); } ); } } ngOnDestroy() { this.numbersObsSubscription.unsuscribe(); }
В конце при уничтожении компонента обязательно отписываемся.
Более сложные, где все типы обработки задействованы:
... customObsSubscription: Subscription; constructor() { } ngOnInit() { const myObservable = Observable.create((observer: Observer<string>) => { setTimeout(() => { observer.next('first package'); }, 2000); setTimeout(() => { observer.next('second package'); }, 4000); setTimeout(() => { // observer.error('this does not work'); observer.complete(); }, 5000); setTimeout(() => { observer.next('third package'); }, 6000); }); this.customObsSubscription = myObservable.subscribe( (data: string) => { console.log(data); }, (error: string) => { console.log(error); }, () => { console.log('completed'); } ); } ngOnDestroy() { this.customObsSubscription.unsubscribe(); }
Пример BehaviorSubject
В файле-сервисе:
rooms: Room[] = [{roomName: 'bath'}, {roomName: 'bedroom'}] private data$$ = new BehaviorSubject<Room[]>(this.rooms); // при создании передаем первичные данные public getRooms$(): Observable<Room[]> { return this.data$$.asObservable(); }
А в файле-компоненте подписываемся, как и на любой Observable:
ngOnInit(): void { this.roomsService.getRooms$().subscribe((rooms) => (this.rooms = rooms)); }
Связь компонентов при помощи Subject
Это более правильный способ связи компонетов, которые не знают друг о друге, чем eventEmiter!
1. Subject в сервисе
Subject – это одновременно observable и observe (наблюдаемый и наблюдатель).Поэтому он подходит для связи двух компонентов.
Создадим Subject в сервисе /app/test.service.ts:
import { Subject } from 'rxjs'; export class TestService { userActivated = new Subject<someType>(); }
2. Компонент, отправляющий данные
Теперь в одном из компонент, где, например, мы используем id в качестве параметра, в шаблоне добавим кнопку:
<button (click)="ActivateObservable()">Activate observable</button>
А в классе пропишем функцию для этой кнопки, которая пушит данные в наш созданный Subject:
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Params } from '@angular/router'; import { TestService } from '../test.service'; @Component({ selector: 'app-location', templateUrl: './location.component.html', styleUrls: ['./location.component.scss'], }) export class LocationComponent implements OnInit { id: number = 0; constructor(private route: ActivatedRoute, private testService: TestService) {} ngOnInit(): void { this.route.params.subscribe((params: someType) => { this.id = +params['id']; this.furnitureServices.testGetIdFromComponent(this.id); }); } ActivateObservable() { this.testService.userActivated.next(this.id); } }
3. Компонент, принимающий данные
Теперь, например, в компоненте, где у нас список элементов, которые имеют ссылки с этими передаваемыми id, принимаем наши данные:
import { Component, OnInit } from '@angular/core'; import { TestService } from '../test.service'; @Component({ selector: 'app-move-detalis', templateUrl: './move-detalis.component.html', styleUrls: ['./move-detalis.component.scss'], }) export class MoveDetalisComponent implements OnInit { nav1ElementActived = false; nav2ElementActived = false; constructor(private testService: TestService) {} ngOnInit(): void { this.testService.userActivated.subscribe((id: unknown | undefined) => { //number не проходит(( if (id === 1) { this.nav1ElementActived = true; } else if (id === 2) { this.nav2ElementActived = true; } }); } }
В шаблоне, зависимо от активированного элемента можно что-то вывести, например слово напротив «activated».
Операторы
Операторы – это легко подключаемые функции, которые обрабатывают данные приходящие внутри подписки. Например, это может понадобиться, когда на беке и фронте разный стиль написания переменных внутри объекта. Но на самом деле операторов очень много в документации.
Например, популярным оператором map обработаем приходящие данные из «простого» Observable и трансформируем их, умножив каждый результат на 2:
ngOnInit() { Observable.interval(1000).pipe( .map( (data: number) => { return data * 2; } )); this.numbersObsSubscription = myNumbers.subscribe( (number: number) => { console.log(number); } ); }