Оновлення 2016-06-27: замість Observables використовуйте будь-яке
- BehaviorSubject, як рекомендує @Abdulrahman у коментарі, або
- ReplaySubject, як рекомендує @Jason Goemaat у коментарі
Предмет є як Спостережувані (так що ми можемо subscribe()
до нього) і спостерігач (так що ми можемо назвати next()
на ній , щоб випромінювати нове значення). Ми використовуємо цю особливість. Тема дозволяє значенням бути багатоадресною для багатьох спостерігачів. Ми не використовуємо цю функцію (у нас є лише один спостерігач).
BehaviorSubject - це варіант Subject. Він має поняття "поточне значення". Ми використовуємо це: щоразу, коли ми створюємо ObservingComponent, воно автоматично отримує поточне значення елемента навігації від BehaviorSubject.
Нижче наведений код та планк використовують BehaviorSubject.
ReplaySubject - це ще один варіант Subject. Якщо ви хочете зачекати, поки значення буде фактично створено, використовуйте ReplaySubject(1)
. Тоді як BehaviorSubject вимагає початкового значення (яке буде надано негайно), ReplaySubject цього не робить. ReplaySubject завжди надаватиме останнє значення, але оскільки він не має необхідного початкового значення, сервіс може виконати деяку операцію асинхронізації, перш ніж повернути перше значення. Він все одно негайно запуститься при наступних дзвінках із останнім значенням. Якщо ви хочете лише одне значення, скористайтеся first()
підпискою. Якщо ви користуєтесь, не потрібно скасовувати підписку first()
.
import {Injectable} from '@angular/core'
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
@Injectable()
export class NavService {
// Observable navItem source
private _navItemSource = new BehaviorSubject<number>(0);
// Observable navItem stream
navItem$ = this._navItemSource.asObservable();
// service command
changeNav(number) {
this._navItemSource.next(number);
}
}
import {Component} from '@angular/core';
import {NavService} from './nav.service';
import {Subscription} from 'rxjs/Subscription';
@Component({
selector: 'obs-comp',
template: `obs component, item: {{item}}`
})
export class ObservingComponent {
item: number;
subscription:Subscription;
constructor(private _navService:NavService) {}
ngOnInit() {
this.subscription = this._navService.navItem$
.subscribe(item => this.item = item)
}
ngOnDestroy() {
// prevent memory leak when component is destroyed
this.subscription.unsubscribe();
}
}
@Component({
selector: 'my-nav',
template:`
<div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
<div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>`
})
export class Navigation {
item = 1;
constructor(private _navService:NavService) {}
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this._navService.changeNav(item);
}
}
Plunker
Оригінальна відповідь, що використовує спостережуване: (для цього потрібно більше коду та логіки, ніж використання BehaviorSubject, тому я не рекомендую його, але це може бути повчально)
Отже, ось реалізація, яка використовує Observable замість EventEmitter . На відміну від моєї реалізації EventEmitter, ця реалізація також зберігає вибране navItem
в сервісі наразі , так що коли створений компонент спостереження, він може отримати поточне значення за допомогою виклику API navItem()
, а потім повідомити про зміни через navChange$
спостережуване.
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/share';
import {Observer} from 'rxjs/Observer';
export class NavService {
private _navItem = 0;
navChange$: Observable<number>;
private _observer: Observer;
constructor() {
this.navChange$ = new Observable(observer =>
this._observer = observer).share();
// share() allows multiple subscribers
}
changeNav(number) {
this._navItem = number;
this._observer.next(number);
}
navItem() {
return this._navItem;
}
}
@Component({
selector: 'obs-comp',
template: `obs component, item: {{item}}`
})
export class ObservingComponent {
item: number;
subscription: any;
constructor(private _navService:NavService) {}
ngOnInit() {
this.item = this._navService.navItem();
this.subscription = this._navService.navChange$.subscribe(
item => this.selectedNavItem(item));
}
selectedNavItem(item: number) {
this.item = item;
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
@Component({
selector: 'my-nav',
template:`
<div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
<div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>
`,
})
export class Navigation {
item:number;
constructor(private _navService:NavService) {}
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this._navService.changeNav(item);
}
}
Plunker
Дивіться також приклад «Кулінарної книги з взаємодії компонентів» , в якому використовується Subject
додаткові дані для спостереження. Хоча приклад "спілкування батьків та дітей", однакова техніка застосовується і для непов'язаних компонентів.