我有一个从API获取数据的角度2服务 此服务有3个订阅者(在“组件”中定义),每个订阅者使用数据(不同的图表)执行其他操作
我注意到我正在向API发出三个GET请求,而我想要实现的是一个请求并且订阅者将共享数据 我查看了HOT和COLD可观察对象并在observable上尝试了.share(),但我仍在进行3次单独调用
更新,添加代码
服务
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import {Observable} from 'rxjs/Rx';
// Import RxJs required methods
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { StationCompliance } from './model/StationCompliance';
@Injectable()
export class StationComplianceService {
private url = '/api/read/stations';
constructor(private http : Http) {
console.log('Started Station compliance service');
}
getStationCompliance() : Observable<StationCompliance []> {
return this.http.get(this.url)
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server Error'));
}
}
组件1
import { Component, OnInit } from '@angular/core';
import { CHART_DIRECTIVES } from 'angular2-highcharts';
import { StationComplianceService } from '../station-compliance.service';
@Component({
selector: 'app-up-down-graph',
templateUrl: './up-down-graph.component.html',
styleUrls: ['./up-down-graph.component.css']
})
export class UpDownGraphComponent implements OnInit {
graphData;
errorMessage: string;
options;
constructor(private stationService : StationComplianceService) { }
ngOnInit() {
this.getStationTypes();
}
getStationTypes(){
this.stationService.getStationCompliance()
.subscribe(
graphData => {
this.graphData = graphData;
this.options = {
chart : {type: 'pie',
plotShadow: true
},
plotOptions : {
showInLegend: true
},
title : {text: 'Up and Down devices'},
series: [{
data: this.processStationType(this.graphData)
}]
}
},
error => this.errorMessage = <any>error
);
}
其他两个组件几乎相同,只是显示其他图形
答案 0 :(得分:32)
我遇到了类似的问题并使用Aran的建议来解决它,以引用Cory Rylan的Angular 2 Observable Data Services博客文章。我的关键是使用se = TestCase.settings;
。以下是最终对我有用的代码片段。
数据服务创建内部BehaviorSubject
以在初始化服务时缓存数据。消费者使用BehaviorSubject
方法访问数据。
subscribeToDataService()
零件:
组件可以在初始化时订阅数据服务。
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
}
组件模板:
然后,模板可以根据需要使用异步管道迭代observable。
export class TestComponent implements OnInit {
allData$: Observable<Data[]>;
constructor(private dataService: DataService) {
}
ngOnInit() {
this.allData$ = this.dataService.subscribeToDataService();
}
}
每次在数据服务中的 *ngFor="let data of allData$ | async"
上调用next()
方法时,都会更新订阅者。
答案 1 :(得分:4)
您的代码中存在的问题是,每次调用函数时都会返回一个新的observable。这是因为http.get
每次调用时都会创建一个新的Observable。解决这个问题的方法可能是在服务中存储observable(通过闭包),这将确保所有主题都订阅相同的observable。这不是完美的代码,但我有一个类似的问题,这暂时解决了我的问题。
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import {Observable} from 'rxjs/Rx';
// Import RxJs required methods
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { StationCompliance } from './model/StationCompliance';
@Injectable()
export class StationComplianceService {
private url = '/api/read/stations';
constructor(private http : Http) {
console.log('Started Station compliance service');
}
private stationComplianceObservable: Rx.Observable<StationCompliance[]>;
getStationCompliance() : Observable<StationCompliance []> {
if(this.stationComplianceObservable){
return this.stationComplianceObservable;
}
this.stationComplianceObservable = this.http.get(this.url)
.debounce(1000)
.share()
.map((res:Response) => res.json())
.finally(function () { this.stationComplianceObservable = null})
.catch((error:any) => Observable.throw(error.json().error || 'Server Error'));
return this.stationComplianceObservable;
}
}
答案 2 :(得分:1)
您可以创建一个被动数据服务并定义一个本地Observable变量,该变量在内部更新,订阅者可以自行更新。 本文正确解释了它 data services
答案 3 :(得分:0)
解决方案是一旦创建了observable就保存并使其可共享(默认情况下不是)。所以你的服务看起来像是:
@Injectable()
export class StationComplianceService {
private stationCompliance: StationCompliance;
private stream: Observable<StationCompliance []>;
private url = '/api/read/stations';
constructor(private http : Http) {
console.log('Started Station compliance service');
}
getStationCompliance() : Observable<StationCompliance []> {
/** is remote value is already fetched, just return it as Observable */
if (this.stationComliance) {
return Observable.of(this.stationComliance);
}
/** otherwise if stream already created, prevent another stream creation (exactly your question */
if (this.stream) {
return this.stream;
}
/** otherwise run remote data fetching */
this.stream = this.http.get(this.url)
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server Error'))
.share(); /** and make the stream shareable (by default it is not) */
return this.stream;
}
}
答案 4 :(得分:0)
shareReplay
应该立即执行-"(...) valuable in situations where you know you will have late subscribers to a stream that need access to previously emitted values."
答案 5 :(得分:0)
我知道这个线程很旧,但是被接受的答案对我有很大帮助,我想使用反跳,switchmap和hacky的全局通知系统添加一些可能的改进(应该使用适当的ngrx:{{3 }})
该概念的前提是可以使用通知服务将更改推送到所有其他服务,以告知它们获取数据:
export class NotifyerService {
constructor() { }
notifyer: Subject<any> = new Subject
notifyAll() {
console.log("ALL NOTIFIED")
this.notifyer.next("GO")
}
}
使用一个主题,因为在一个主题上调用.next(val)会将数据推送到所有侦听器
在特定组件的服务(在您的情况下为“ DataService”)中,您可以管理数据获取和缓存活动:
export class GetDataService {
// cache the incoming data
cache: Subject<any> = new Subject
constructor(private http: HttpClient,
private notifyerService: NotifyerService) {
// subscribe to any notification from central message broker
this.notifyerService.notifyer.pipe(
// filtering can be used to perform different actions based on different notifications
filter(val => val == "GO"),
// prevent notification spam by debouncing
debounceTime(300),
// SUBSCRIBE to the output of getData, cancelling previous subscription
switchMap(() => this.getData())
).subscribe(res => {
console.log("expensive server request")
// save data in cache, notify listeners
this.cache.next(res)
})
}
// expensive function which gets data
getData(): Observable<any> {
return this.http.get<any>(BASE_URL);
}
}
以上代码中的关键概念是您设置一个缓存对象,并在有通知时对其进行更新。在构造函数中,我们希望通过一系列运算符通过管道传递所有将来的通知:
现在所有这些,服务器数据将通过.next(res)方法放置在缓存中。
现在所有最终组件所需要做的就是监听缓存以进行更新,并适当地进行处理:
export class ButtonclickerComponent implements OnInit {
value: any;
constructor(private getDataService: GetDataService,
private notifyerService: NotifyerService) { }
ngOnInit() {
// listen to cache for updates
this.getDataService.cache.pipe(
// can do something specific to this component if have multiple subscriptions off same cache
map(x => x)
// subsc
).subscribe(x => { console.log(x); this.value = x.value })
}
onClick() {
// notify all components of state changes
this.notifyerService.notifyAll()
}
}
实际的概念: