Pembaruan 2016-06-27: alih-alih menggunakan Observables, gunakan keduanya
- sebuah BehaviorSubject, seperti yang direkomendasikan oleh @Abdulrahman dalam komentar, atau
- sebuah ReplaySubject, seperti yang direkomendasikan oleh @Jason Goemaat dalam komentar
Sebuah Subyek adalah baik diamati (sehingga kita bisa subscribe()
untuk itu) dan Pengamat (sehingga kita bisa menyebutnya next()
di atasnya untuk memancarkan nilai baru). Kami memanfaatkan fitur ini. Subjek memungkinkan nilai menjadi multicast bagi banyak Pengamat. Kami tidak mengeksploitasi fitur ini (kami hanya memiliki satu Pengamat).
BehaviorSubject adalah varian dari Subjek. Ini memiliki gagasan "nilai saat ini". Kami mengeksploitasi ini: setiap kali kami membuat ObservingComponent, ia mendapatkan nilai item navigasi saat ini dari BehaviorSubject secara otomatis.
Kode di bawah ini dan plunker menggunakan BehaviorSubject.
ReplaySubject adalah varian lain dari Subjek. Jika Anda ingin menunggu hingga nilai benar-benar dihasilkan, gunakan ReplaySubject(1)
. Sedangkan BehaviorSubject membutuhkan nilai awal (yang akan segera disediakan), ReplaySubject tidak. ReplaySubject akan selalu memberikan nilai terbaru, tetapi karena tidak memiliki nilai awal yang diperlukan, layanan dapat melakukan beberapa operasi async sebelum mengembalikan nilai pertama. Itu akan tetap aktif segera pada panggilan berikutnya dengan nilai terbaru. Jika Anda hanya ingin satu nilai, gunakan first()
pada berlangganan. Anda tidak harus berhenti berlangganan jika menggunakan 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
Jawaban asli yang menggunakan Observable: (memerlukan lebih banyak kode dan logika daripada menggunakan BehaviorSubject, jadi saya tidak merekomendasikannya, tetapi mungkin instruktif)
Jadi, inilah implementasi yang menggunakan Observable alih-alih EventEmitter . Tidak seperti implementasi EventEmitter saya, implementasi ini juga menyimpan yang saat ini dipilih navItem
dalam layanan, sehingga ketika komponen mengamati dibuat, itu dapat mengambil nilai saat ini melalui panggilan API navItem()
, dan kemudian diberitahu tentang perubahan melalui 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
Lihat juga contoh Cookbook Interaksi Komponen , yang menggunakan Subject
selain yang bisa diamati. Meskipun contohnya adalah "komunikasi orang tua dan anak-anak," teknik yang sama berlaku untuk komponen yang tidak terkait.