angularrxjsangular-httpclientsubject-observer

How can I use the same observable with multiple mappings?


My code has a Subject which, when new values are added triggers a HTTP request which returns an Observable.

I want to process this data in two different ways (using the same data) and use the resulting Observables stored in group and times as values to put in ngFor using the async pipe.

While this does work, the HTTP requests are sent multiple times - I only want them to be sent once for each subscription.

Below is a minimal example.

import { Component, OnInit } from "@angular/core";
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";

import { ExampleService } from "../example.service";

import "rxjs/add/operator/switchMap";

@Component({
  templateUrl: "./example.component.html",
  styleUrls: ["./example.component.scss"]
})
export class ExampleComponent implements OnInit {

  constructor(
    private exampleService: ExampleService
  ) { }

  ngOnInit() {

    var selections = new Subject<string>();

    var appointments = selections
      // exampleService.getData returns an HTTP observable.
      .switchMap(date => this.exampleService.getData(date));

    var group = appointments
      .map(data => this.process(data));

    var times = appointments
      .map(data => this.calculateTimes(data));

    // Calling subscribe each time sends the HTTP request multiple
    // times - I only want it to be send once for both of them: they
    // can share the data!!
    group.subscribe();
    times.subscribe();

    // selections.next(someData) is called periodically from some
    // omitted code.
  }

  processExample(data: string[]) {
    /*
     * Some processing code.
    */

    return data;
  }

  calculateTimes(data: string[]) {
    /*
     * Some processing code.
    */

    return data;
  }
}

What is the best way to achieve this?


Solution

  • You can use the share operator to archive what you are looking for:

    import 'rxjs/add/operator/share';
    
    ngOnInit() {
    
      var selections = new Subject<string>();
    
      var appointments = selections
        // exampleService.getData returns an HTTP observable.
        .switchMap(date => this.exampleService.getData(date))
        .share();
    
      var group = appointments
        .map(data => this.process(data));
    
      var times = appointments
        .map(data => this.calculateTimes(data));
    
      // Calling subscribe each time sends the HTTP request multiple
      // times - I only want it to be send once for both of them: they
      // can share the data!!
      group.subscribe();
      times.subscribe();
    
      // selections.next(someData) is called periodically from some
      // omitted code.
    }
    

    Basically, the different observers share the same source among them.

    More info about the operator here.