2016-09-21 15 views
17

Mam kątową 2 usługi, pobrać dane z API usługa ta 3 abonentów (zdefiniowanego w komponenty) każdy robi coś innego z danymi (różne wykresy)kątowa 2 Obserwowalne z wieloma abonentami

jestem zauważając, że wykonuję trzy żądania GET do API, podczas gdy to, co chcę osiągnąć, to jedno żądanie i że subskrybenci będą udostępniać dane Rozglądam się za HOT i COLD obserwowalnym i wypróbowałem .share() na obserwowalnym, ale Wciąż wykonuję 3 indywidualne połączenia

Aktualizacja, dodawanie kodu

usługi

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')); 
    } 
} 

Komponent 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 
    ); 
    } 

Pozostałe dwa składniki są prawie takie same, po prostu pokazać inny wykres

Odpowiedz

20

I napotkał podobny problem i rozwiązać go za pomocą sugestii Aran, aby odwołać Cory Rylan za Angular 2 Observable Data Services blogu. Kluczem dla mnie było użycie BehaviorSubject. Oto fragmenty kodu, które ostatecznie dla mnie działały.

Usługa danych:

Usługa danych tworzy wewnętrzną funkcję BehaviorSubject w celu buforowania danych raz podczas inicjowania usługi. Konsumenci używają metody subscribeToDataService() w celu uzyskania dostępu do danych.

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 

    } 
Składnik:

komponenty mogą zapisać się do usługi danych po inicjalizacji.

export class TestComponent implements OnInit { 
     allData$: Observable<Data[]>; 

     constructor(private dataService: DataService) { 
     } 

     ngOnInit() { 
     this.allData$ = this.dataService.subscribeToDataService(); 
     } 

    } 
Składnik Szablon:

Szablon może następnie iteracyjnego obserwowalny w razie potrzeby za pomocą rury asynchronicznej.

*ngFor="let data of allData$ | async" 

Subskrybenci są aktualizowani za każdym razem, gdy wywoływana jest metoda next() na BehaviorSubject w usłudze danych.

+0

Dzięki kod naprawdę pomogło mi – naoru

+0

I ciągnął mnie za włosy na temat tego „problemu”, a ten post made my day. Dzięki – sjosen

+0

Proszę rozważyć krótsze rozwiązanie: https://stackoverflow.com/questions/39627396/angular-2-observable-with-multiple-subscribers/48803113#48803113 – zeliboba

1

można utworzyć reaktywny usługi danych i zdefiniuj lokalną obserwowalną zmienną, która jest aktualizowana wewnętrznie i subskrybuje bers mogą aktualizować się. ten artykuł wyjaśnia to właściwie data services

1

Kwestia, którą masz w swoim kodzie, polega na tym, że zwracasz nowe obserwowalne za każdym razem, gdy wywoływana jest twoja funkcja. Dzieje się tak, ponieważ http.get tworzy nowe obserwowalne za każdym razem, gdy jest wywoływane. Sposobem na rozwiązanie tego problemu może być przechowywanie obserwowalnego (poprzez zamknięcie) w usłudze, która zapewni, że wszyscy badani subskrybują to samo, co można zaobserwować. To nie jest doskonały kod, ale miałem podobny problem i na razie rozwiązał mój problem.

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; 
    } 
} 
+0

Ta odpowiedź ma na pewno wartość, dzięki. Ale myślę, że powinieneś wyjaśnić użycie '.share()', ponieważ ma to kluczowe znaczenie dla unikania duplikowania połączeń. Odpowiedź jest dobra, jeśli chcesz uniknąć asynchronicznych wywołań tego samego punktu końcowego interfejsu API. Zaakceptowana odpowiedź może zapewnić buforowaną odpowiedź nawet dłużej niż czas trwania wywołania backendu. –

+0

Tak, to nie jest świetne rozwiązanie. Przyjęta odpowiedź jest znacznie lepsza. –

0

Rozwiązanie jest zapisywane raz utworzone i możliwe do udostępnienia (domyślnie nie jest).Więc twój serwis będzie wyglądać następująco:

@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; 
    } 
}