I encountered a similar problem and solved it using Aran’s suggestion to reference Cory Rylan’s Angular 2 Observable Data Services blog post. The key for me was using BehaviorSubject
. Here’s the snippets of the code that ultimately worked for me.
Data Service:
The data service creates an internal BehaviorSubject
to cache the data once when the service is initialized. Consumers use the subscribeToDataService()
method to access the data.
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';
import { Data } from './data';
import { properties } from '../../properties';
@Injectable()
export class DataService {
allData: Data[] = new Array<Data>();
allData$: BehaviorSubject<Data[]>;
constructor(private http: Http) {
this.initializeDataService();
}
initializeDataService() {
if (!this.allData$) {
this.allData$ = <BehaviorSubject<Data[]>> new BehaviorSubject(new Array<Data>());
this.http.get(properties.DATA_API)
.map(this.extractData)
.catch(this.handleError)
.subscribe(
allData => {
this.allData = allData;
this.allData$.next(allData);
},
error => console.log("Error subscribing to DataService: " + error)
);
}
}
subscribeToDataService(): Observable<Data[]> {
return this.allData$.asObservable();
}
// other methods have been omitted
}
Component:
Components can subscribe to the data service upon initialization.
export class TestComponent implements OnInit {
allData$: Observable<Data[]>;
constructor(private dataService: DataService) {
}
ngOnInit() {
this.allData$ = this.dataService.subscribeToDataService();
}
}
Component Template:
The template can then iterate over the observable as necessary using the async pipe.
*ngFor="let data of allData$ | async"
Subscribers are updated each time the next()
method is called on the BehaviorSubject
in the data service.