64 votes

Angular 2 Observable avec plusieurs abonnés

J'ai angulaire 2 service d'extraction de données à partir d'une API ce service dispose de 3 abonnés (défini dans les Composants) chacun de faire autre chose avec les données (graphiques)

Je m'aperçois que je suis en train de faire trois demandes pour de l'API, tout ce que je veux réaliser est une demande et que les abonnés pourront partager les données J'ai regarde dans le CHAUD et le FROID observables et j'ai essayé le .action() sur les observables, mais je suis encore en train de faire 3 appels individuels

Mise à jour, ajout de code

Service

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

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

Deux autres composants sont presque les mêmes, ils ont juste montrer aux autres graphique

53voto

Scott Points 414

J'ai rencontré un problème similaire et résolu à l'aide d'Aran propose de référence Cory Rylan du Angulaire 2 Données Observables Services de blog. Pour moi, l'essentiel était l'aide d' BehaviorSubject. Voici les extraits du code qui a finalement fonctionné pour moi.

Service De Données:

Le service de données crée un interne BehaviorSubject mettre en cache les données une seule fois lorsque le service est initialisé. Les consommateurs utilisent l' subscribeToDataService() méthode d'accès aux données.

    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

    }

Composant:

Les composants peuvent s'abonner au service de données lors de l'initialisation.

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

      constructor(private dataService: DataService) {
      }

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

    }

Modèle De Composant:

Le modèle peut ensuite effectuer une itération sur les observables que nécessaire en utilisant la async pipe.

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

Les abonnés sont mis à jour chaque fois que l' next() méthode est appelée sur l' BehaviorSubject dans le service de données.

8voto

Ray Sülzer Points 1040

Le problème que vous avez dans votre code, c'est que vous êtes de retour d'un nouveau observable à chaque fois que votre fonction est appelée. C'est parce qu' http.get est la création d'un nouveau Observable à chaque fois qu'il est appelé. La façon de résoudre ce pourrait être pour stocker les observables (par l'intermédiaire fermeture) dans le service qui va s'assurer que tous les sujets de la souscription de la même observable. Ce n'est pas parfait code, mais j'ai eu un problème similaire et cela a résolu mon problème pour le moment.

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

2voto

1voto

Aran Dekar Points 296

vous pouvez créer un service de données réactif et définir une variable observable locale qui est mise à jour en interne et que les abonnés peuvent mettre à jour eux-mêmes. cet article explique correctement les services de données

-1voto

zeliboba Points 717

La solution est sauvegardée une fois créée observable et rendue partageable (par défaut ce n’est pas). Votre service ressemblera donc à:

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X