2

I'm trying to make a module with components that share a service. One component (graphview) has a subscription to an observable of a BehaviorSubject. Another component (chart-type) later updates the BehaviorSubject by calling a function in the service (chart-config). The problem is when ChartTypeComponent has the service call .next(data) the GraphviewComponent doesn't update. I'm assuming that multiple instances of the service are being used, and I'm not sure how to fix it.

GraphModule (their parent module) has ChartTypeComponent and GraphviewComponent in declarations and has ChartConfigService in providers.

chart-config.service.ts looks like:

@Injectable()
export class ChartConfigService {
  private _chartconfig: BehaviorSubject<any> = new BehaviorSubject<any>({});
  public chartconfig = this._chartconfig.asObservable();
  private headers = new Headers({ 'Content-Type': 'application/json', 'charset': 'UTF-8' });
  private options = new RequestOptions({ headers: this.headers });

  constructor(private http: Http) {
      http.get('/api/chartconfig/59484e3946f7f059f1e72927')
      .map(res => res.json())
      .subscribe( data => this._chartconfig.next(data) );
    }

  getChartConfig(): Observable<any> {
    return this.chartconfig;
  }

  updateChartConfig(option, params) {
    this.http.put('/api/chartconfig/59484e3946f7f059f1e72927', '{ ' + JSON.stringify(option) + ': ' + JSON.stringify(params) + ' }' , this.options)
      .map(res => res.json())
      .subscribe( data => this._chartconfig.next(data) );
  }

The graphview.component.ts imports the service and subscription. It doesn't provide the service.

@Component({
  selector: 'app-graphview',
  templateUrl: './graphview.component.html',
  styleUrls: ['./graphview.component.scss']
})

and

  constructor(private chartconfigService: ChartConfigService) { }

  ngOnInit() {
    this.subscription = this.chartconfigService.chartconfig
      .subscribe( data => this.localChartConfig = data );
  }

the chart-type.component.ts imports the service only, and calls the chartconfigService.updateChartConfig:

@Component({
  selector: 'app-chart-type',
  templateUrl: './chart-type.component.html',
  styleUrls: ['./chart-type.component.scss']
})

and

  chartType(param: string) {
    this.chartconfigService.updateChartConfig('type', param);
  }

I've found relevant questions and answers:

Delegation: EventEmitter or Observable in Angular2

Angular2 Observable BehaviorSubject service not working

but I can't figure out what I'm doing wrong. How can I get graphview.component.ts to update when the updateChartConfig is called?

note: All the call/methods/etc work, and after updateChartConfig() is called this._chartconfig.next(data).getValue() shows that the BehaviorSubject is being updated properly within the service. The graphview.component.ts simply isn't receiving the new data.

Update:

Results of changing GraphviewComponents ngOnInit() to

  ngOnInit() {
    this.subscription = this.chartconfigService.chartconfig
      .subscribe( data => { this.localChartConfig = data; console.log('in subscription: ' + this.localChartConfig); }, e => console.log('error: ' + e), () => console.log('then: ' + this.localChartConfig) );
  }

Produces in console:

in subscription: [object Object]
in subscription: [object Object]

But is never proc'd after OnInit (when ChartTypeComponent calls updateChartConfig() ).

Update2: It's clear the root is injecting the service, and not the GraphModule: Service Tree

Service Tree 2

I'd still like the submodule to provide the service so that it's contained within the submodule.

Maciej Treder
  • 9,856
  • 4
  • 40
  • 67
Z. Bagley
  • 7,705
  • 1
  • 30
  • 46
  • As a sanity check, what happens if you place a console.log statement in the graphview component that should write to the console when the observable it is subscribed to sends an event? – Rob Zuber Jun 21 '17 at 00:12
  • It doesn't get triggered, did check that one as well. Updated question to reflect. – Z. Bagley Jun 21 '17 at 00:13
  • 1
    Nothing is jumping out at me. It might help to post the NgModule code so we could see that. Also, have you tried the Augury Chrome extension? It visually shows your components and services. https://augury.angular.io/ – Rob Zuber Jun 21 '17 at 00:23
  • What happens if you also inject the service into the ChartType component? – Rob Zuber Jun 21 '17 at 00:32
  • Nothing changes if I list it as a provider. At this point I'm going to just let to root module provide the service instead of attempting to get the graph module to provide it.. though I would still like to know how to get the submodule to provide the singleton service. – Z. Bagley Jun 21 '17 at 00:38
  • OK, yes I believe listing it as a provider creates a new instance on purpose. But is the service injected into the ChartType component constructor? – Rob Zuber Jun 21 '17 at 00:39
  • Yes it is, it has to be in order to access the updateChartConfig() method – Z. Bagley Jun 21 '17 at 00:40
  • I'm voting to close this question as off-topic because this question does not have a solution for Angular 2-5, and has a hard-coded solution in Angular 6. No longer relevant (and my own question). I believe the question should still remain archived and closed, but not be deleted. – Z. Bagley Jul 18 '18 at 16:28
  • Note: The solution in Angular 6 is their built in tree-shaking with declarations of @Injectable to their specific module (now has a place in Angular's core API). – Z. Bagley Jul 18 '18 at 16:29

0 Answers0