Обновление 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
Оригинальный ответ, который использует Observable: (он требует больше кода и логики, чем использование BehaviorSubject, поэтому я не рекомендую его, но это может быть поучительно)
Итак, вот реализация, которая использует Observable вместо EventEmitter . В отличие от моей реализации EventEmitter, эта реализация также сохраняет navItem
текущий выбранный в сервисе, так что когда создается компонент наблюдения, он может получить текущее значение с помощью вызова API navItem()
, а затем получать уведомления об изменениях через navChange$
Observable.
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
в дополнение к наблюдаемым используется. Хотя примером является «общение между родителями и детьми», тот же метод применим для несвязанных компонентов.