page - router events angular 6
Angular 2 observable con mĂșltiples suscriptores (5)
Tengo un servicio angular 2 que recupera datos de una API. Este servicio tiene 3 suscriptores (definidos en Componentes), cada uno haciendo otra cosa con los datos (diferentes gráficos)
Me doy cuenta de que estoy haciendo tres solicitudes GET a la API mientras que lo que quiero lograr es una solicitud y que los suscriptores compartirán los datos que he visto en HOT y COLD observables y probé .share () en el observable pero todavía estoy haciendo 3 llamadas individuales
Actualizar, agregando código
Servicio
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''));
}
}
Componente 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
);
}
Otros dos componentes son casi iguales, solo muestran otra gráfica
El problema que tiene en su código es que está devolviendo un nuevo observable cada vez que se llama a su función. Esto se debe a que http.get
está creando un nuevo Observable cada vez que se llama. La forma de resolver esto podría ser almacenar el observable (a través del cierre) en el servicio, lo que garantizará que todos los sujetos se suscriban al mismo observable. Este no es un código perfecto, pero tuve un problema similar y esto resolvió mi problema por el momento.
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;
}
}
Encontré un problema similar y lo resolví utilizando la sugerencia de Aran para hacer referencia a la publicación del blog Angular 2 Observable Data Services de Cory Rylan. La clave para mí fue usar BehaviorSubject. Aquí están los fragmentos del código que finalmente funcionó para mí.
Servicio de datos:El servicio de datos crea un BehaviorSubject interno para almacenar en caché los datos una vez cuando se inicializa el servicio. Los consumidores utilizan el método subscribeToDataService () para acceder a los datos.
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
}
Componente:
Los componentes pueden suscribirse al servicio de datos en el momento de la inicialización.
export class TestComponent implements OnInit {
allData$: Observable<Data[]>;
constructor(private dataService: DataService) {
}
ngOnInit() {
this.allData$ = this.dataService.subscribeToDataService();
}
}
Plantilla de componente:
La plantilla puede entonces iterar sobre lo observable según sea necesario utilizando el conducto asíncrono.
*ngFor="let data of allData$ | async"
Los suscriptores se actualizan cada vez que se llama al método next () en el BehaviorSubject en el servicio de datos.
La solución es guardar una vez creado observable y hacer que se pueda compartir (por defecto no lo es). Para que su servicio se vea como:
@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;
}
}
puede crear un servicio de datos reactivos y definir una variable observable local que se actualiza internamente y los suscriptores pueden actualizarse ellos mismos. Este artículo lo explica adecuadamente los servicios de datos.