Update 27.06.2016: Verwenden Sie anstelle von Observables entweder
- ein BehaviorSubject, wie von @Abdulrahman in einem Kommentar empfohlen, oder
- ein ReplaySubject, wie von @Jason Goemaat in einem Kommentar empfohlen
Ein Subjekt ist sowohl ein Observable (damit wir es erreichen können subscribe()
) als auch ein Observer (damit wir es aufrufen next()
können, um einen neuen Wert auszugeben). Wir nutzen diese Funktion. Ein Betreff ermöglicht das Multicasting von Werten an viele Beobachter. Wir nutzen diese Funktion nicht aus (wir haben nur einen Beobachter).
BehaviorSubject ist eine Variante von Subject. Es hat den Begriff "der aktuelle Wert". Wir nutzen dies aus: Wenn wir eine ObservingComponent erstellen, wird der aktuelle Wert des Navigationselements automatisch vom BehaviorSubject abgerufen.
Der folgende Code und der Plunker verwenden BehaviorSubject.
ReplaySubject ist eine weitere Variante von Subject. Wenn Sie warten möchten, bis ein Wert tatsächlich erzeugt wird, verwenden Sie ReplaySubject(1)
. Während für ein BehaviorSubject ein Anfangswert erforderlich ist (der sofort bereitgestellt wird), ist dies bei ReplaySubject nicht der Fall. ReplaySubject liefert immer den neuesten Wert. Da jedoch kein erforderlicher Anfangswert vorhanden ist, kann der Dienst eine asynchrone Operation ausführen, bevor der erste Wert zurückgegeben wird. Bei nachfolgenden Aufrufen mit dem neuesten Wert wird es immer noch sofort ausgelöst. Wenn Sie nur einen Wert möchten, verwenden Sie ihn first()
für das Abonnement. Sie müssen sich nicht abmelden, wenn Sie verwenden 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
Ursprüngliche Antwort, die ein Observable verwendet: (Es erfordert mehr Code und Logik als die Verwendung eines BehaviorSubject, daher empfehle ich es nicht, aber es kann lehrreich sein.)
Hier ist also eine Implementierung, die ein Observable anstelle eines EventEmitter verwendet . Im Gegensatz zu meiner EventEmitter-Implementierung speichert diese Implementierung auch die aktuell navItem
im Service ausgewählten Elemente , sodass beim Erstellen einer Beobachtungskomponente der aktuelle Wert per API-Aufruf abgerufen navItem()
und über Änderungen über navChange$
Observable benachrichtigt werden kann .
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
Siehe auch das Beispiel des Component Interaction Cookbook , Subject
in dem zusätzlich zu Observables ein verwendet wird. Obwohl das Beispiel "Kommunikation zwischen Eltern und Kindern" lautet, gilt dieselbe Technik für nicht verwandte Komponenten.