Actualización 2016-06-27: en lugar de usar Observables, use cualquiera
- un BehaviorSubject, según lo recomendado por @Abdulrahman en un comentario, o
- un ReplaySubject, según lo recomendado por @Jason Goemaat en un comentario
Un Sujeto es tanto un Observable (para que podamos subscribe()
hacerlo) como un Observador (para que podamos invocarlo next()
y emitir un nuevo valor). Explotamos esta característica. Un sujeto permite que los valores sean multidifusión para muchos observadores. No explotamos esta característica (solo tenemos un observador).
BehaviorSubject es una variante de Subject. Tiene la noción de "el valor actual". Explotamos esto: cada vez que creamos un componente Observador, obtiene el valor del elemento de navegación actual del BehaviorSubject automáticamente.
El código a continuación y el plunker usan BehaviorSubject.
ReplaySubject es otra variante de Subject. Si desea esperar hasta que se produzca un valor, use ReplaySubject(1)
. Mientras que BehaviorSubject requiere un valor inicial (que se proporcionará de inmediato), ReplaySubject no. ReplaySubject siempre proporcionará el valor más reciente, pero dado que no tiene un valor inicial requerido, el servicio puede realizar algunas operaciones asíncronas antes de devolver su primer valor. Todavía se disparará inmediatamente en llamadas posteriores con el valor más reciente. Si solo quieres un valor, úsalo first()
en la suscripción. No tiene que darse de baja si lo usa 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
Respuesta original que usa un Observable: (requiere más código y lógica que usar un BehaviorSubject, por lo que no lo recomiendo, pero puede ser instructivo)
Entonces, aquí hay una implementación que usa un Observable en lugar de un EventEmitter . A diferencia de mi implementación EventEmitter, esta implementación también almacena el seleccionado actualmente navItem
en el servicio, de modo que cuando se crea un componente de observación, puede recuperar el valor actual a través de una llamada API navItem()
y luego ser notificado de los cambios a través del 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
Consulte también el ejemplo del Libro de cocina de interacción de componentes , que utiliza Subject
además de observables. Aunque el ejemplo es "comunicación entre padres e hijos", la misma técnica es aplicable para componentes no relacionados.