Aggiornamento 27/06/2016: invece di utilizzare Observables, utilizzare uno dei due
- un oggetto comportamentale, come raccomandato da @Abdulrahman in un commento, oppure
- un oggetto Replay, come raccomandato da @Jason Goemaat in un commento
Un soggetto è sia un osservabile (quindi possiamo subscribe()
farlo ad esso) sia un osservatore (quindi possiamo invocarlo next()
per emettere un nuovo valore). Sfruttiamo questa funzionalità. Un soggetto consente ai valori di essere multicast per molti osservatori. Non sfruttiamo questa funzione (abbiamo un solo osservatore).
BehaviorSubject è una variante di Subject. Ha la nozione di "il valore corrente". Lo sfruttiamo: ogni volta che creiamo un ObservingComponent, ottiene automaticamente il valore dell'elemento di navigazione corrente da BehaviorSubject.
Il codice seguente e il plunker usano BehaviorSubject.
ReplaySubject è un'altra variante di Subject. Se si desidera attendere fino alla produzione effettiva di un valore, utilizzare ReplaySubject(1)
. Mentre un BehaviorSubject richiede un valore iniziale (che verrà fornito immediatamente), ReplaySubject no. ReplaySubject fornirà sempre il valore più recente, ma poiché non ha un valore iniziale richiesto, il servizio può eseguire alcune operazioni asincrone prima di restituire il primo valore. Si attiverà comunque immediatamente sulle chiamate successive con il valore più recente. Se desideri solo un valore, utilizza first()
l'abbonamento. Non è necessario annullare l'iscrizione se si utilizza 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
Risposta originale che utilizza un osservabile: (richiede più codice e logica rispetto all'utilizzo di un oggetto comportamentale, quindi non lo consiglio, ma può essere istruttivo)
Quindi, ecco un'implementazione che utilizza un osservabile anziché un EventEmitter . A differenza della mia implementazione EventEmitter, questa implementazione memorizza anche il servizio attualmente selezionato navItem
nel servizio, in modo che quando viene creato un componente di osservazione, può recuperare il valore corrente tramite chiamata API navItem()
e quindi essere informato delle modifiche tramite l' navChange$
Osservabile.
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
Vedi anche l' esempio del ricettario di interazione dei componenti , che utilizza un Subject
oltre agli osservabili. Sebbene l'esempio sia "comunicazione padre e figlio", la stessa tecnica è applicabile per componenti non correlati.