Mise à jour 2016-06-27: au lieu d'utiliser Observables, utilisez soit
- un BehaviorSubject, comme recommandé par @Abdulrahman dans un commentaire, ou
- un ReplaySubject, comme recommandé par @Jason Goemaat dans un commentaire
Un sujet est à la fois un observable (pour que nous y puissions subscribe()
) et un observateur (pour que nous puissions l'appeler next()
pour émettre une nouvelle valeur). Nous exploitons cette fonctionnalité. Un sujet permet aux valeurs d'être multidiffusées à de nombreux observateurs. Nous n'exploitons pas cette fonctionnalité (nous n'avons qu'un seul Observateur).
BehaviorSubject est une variante de Subject. Il a la notion de "valeur actuelle". Nous exploitons ceci: chaque fois que nous créons un ObservingComponent, il obtient automatiquement la valeur actuelle de l'élément de navigation du BehaviorSubject.
Le code ci-dessous et le plongeur utilisent BehaviorSubject.
ReplaySubject est une autre variante de Subject. Si vous voulez attendre qu'une valeur soit réellement produite, utilisez ReplaySubject(1)
. Alors qu'un BehaviorSubject nécessite une valeur initiale (qui sera fournie immédiatement), ReplaySubject n'en a pas. ReplaySubject fournira toujours la valeur la plus récente, mais comme il n'a pas de valeur initiale requise, le service peut effectuer une opération asynchrone avant de renvoyer sa première valeur. Il se déclenchera toujours immédiatement lors des appels suivants avec la valeur la plus récente. Si vous ne voulez qu'une seule valeur, utilisez-la first()
sur l'abonnement. Vous n'avez pas à vous désinscrire si vous utilisez 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
Réponse originale qui utilise un Observable: (elle nécessite plus de code et de logique que d'utiliser un BehaviorSubject, donc je ne le recommande pas, mais cela peut être instructif)
Voici donc une implémentation qui utilise un Observable au lieu d'un EventEmitter . Contrairement à mon implémentation EventEmitter, cette implémentation stocke également l' navItem
élément actuellement sélectionné dans le service, de sorte que lorsqu'un composant d'observation est créé, il peut récupérer la valeur actuelle via un appel API navItem()
, puis être informé des modifications via l' 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
Voir aussi l' exemple du livre de recettes sur l'interaction des composants , qui utilise un Subject
en plus des observables. Bien que l'exemple soit la «communication parents-enfants», la même technique s'applique aux composants non liés.