Search code examples
angularcomponentsrxjsstore

Angular2 - one store, multiple component, different API call


Let's say I've a parent component, HomeComponent, with multiple nested component, TeamsStandingComponent. TeamsStandingComponent must show data collected from an API call, using a common but private store, TeamsStandingStore.

Now I'll show you my code.

HomeComponent:

import { Component } from '@angular/core';

@Component({
  selector: 'home',
  templateUrl: '../templates/home.html'
})
export class HomeComponent {
  constructor(
  ) {}
}

And this is the HomeComponent template:

<div class="top-three-standings__wrapper">
  <teams-standing #standing1 [leagueId]="426"></teams-standing>
  <teams-standing #standing2 [leagueId]="439"></teams-standing>
</div>

This is the TeamsStandingComponent:

import { Component, AfterViewInit, NgZone, ChangeDetectorRef,
  ElementRef, Input } from '@angular/core';

import { TeamsStandingStore } from '../stores/teams-standing';
import { HttpClient } from '../services/http-client'; // you can ignore this

@Component({
  selector: 'teams-standing',
  providers: [HttpClient], // you can ignore this
  templateUrl: '../templates/teams-standing.html'
})
export class TeamsStandingComponent implements AfterViewInit {

  @Input() private teams: Object;
  @Input() private leagueId: string;
  private teamsStandingStore: TeamsStandingStore;

  constructor(
    private TeamsStandingStore: TeamsStandingStore,
    private ngzone: NgZone,
    private cdref: ChangeDetectorRef
  ) {
    console.clear();
    this.teamsStandingStore = TeamsStandingStore;
  }

  public ngAfterViewInit () {
    this.ngzone.runOutsideAngular( () => {

      this.teamsStandingStore.standings
        .subscribe( (data) => {
          this.teams = data;
          this.cdref.detectChanges();
        } );
    });

    this.http.get(`competitions/` + this.leagueId + `/leagueTable`)
      .subscribe(
        (data: any) => this.teamsStandingStore.showStandings(data.json()),
        (error) => console.log(error)
      );
  }

}

And this is the TeamsStandingComponent template:

<div class="teams-standing__table">
  <h2>{{leagueId}} - {{teams?._links?.competition?.href}}</h2>
  <h3>{{teams?.leagueCaption}}</h3>
  <div *ngFor="let team of teams?.standing">
    {{team.teamName}}
    {{team.crestURI}}
  </div>
</div>

And finally this is the TeamStandingStore:

import { Injectable } from '@angular/core';
import { Subject }  from 'rxjs/Rx';

@Injectable()
export class TeamsStandingStore {

  private standings: Subject<any> = new Subject<any>();
  private showStands: Subject<any> = new Subject<any>();

  constructor() {
    this.showStands
      .subscribe(this.standings);
  }

  public showStandings(standings) {
    this.showStands.next(standings);
  }

}

My issue is that those nested component, TeamsStandingComponent, show the same data, even if every component calls a different endpoint - as you can see - and has a different response.

PS: I'm using @angular v.2.4.9 and rxjs v.5.0.2


Solution

  • I believe all the nested components are getting the same value because they are all using the same instance of the TeamStandingStore.

    You do not show your module where you are providing your TeamStandingStore, but my guess your providing it as the module level. This means every component is getting the same instance of the Store and thus all subscribing to the same standings observable.

    What you may want to do in this case is provide the Store at the component level instead of at the module level so each TeamStandingComponent has it's own instance. You would do this by providing the TeamStandingStore in the component decorator like this:

    @Component({
      selector: 'teams-standing',
      providers: [HttpClient, TeamStandingStore], // <- insert TeamStandingStore here
      templateUrl: '../templates/teams-standing.html'
    })
    export class TeamsStandingComponent implements AfterViewInit {
    
      @Input() private teams: Object;
      @Input() private leagueId: string;
      private teamsStandingStore: TeamsStandingStore;
    
      constructor(
        private TeamsStandingStore: TeamsStandingStore,
        private ngzone: NgZone,
        private cdref: ChangeDetectorRef
      ) {
        console.clear();
        this.teamsStandingStore = TeamsStandingStore;
      }