I've set up a "global" service, to share parameters across components in my Angular app:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@Injectable()
export class ParameterService {
param1 = new BehaviorSubject(null);
publishParam1(data: number) {
this.param1.next(data);
}
param2 = new BehaviorSubject(null);
publishParam2(data: number) {
this.param2.next(data);
}
}
Components which need either or both of these parameters can subscribe, and be notified when these change:
private subscriptions: Array<Subscription> = [];
param1: number; // keep local copies of the params
param2: number;
ngOnInit(): void {
this.subscriptions.push(this._parameterService.param1.subscribe(
data => {
console.log("param1 changed: " + data);
if (data != null) {
this.param1 = data;
// NB! this.param2 may be undefined!
this.getDataFromAPI(this.param1, this.param2);
}
}
));
this.subscriptions.push(this._parameterService.param2.subscribe(
data => {
console.log("param2 changed: " + data);
if (data != null) {
this.param2 = data;
// NB! this.param1 may be undefined!
this.getDataFromAPI(this.param1, this.param2);
}
}
));
}
ngOnDestroy() {
// https://stackoverflow.com/questions/34442693/how-to-cancel-a-subscription-in-angular2
this.subscriptions.forEach((subscription: Subscription) => {
subscription.unsubscribe();
});
}
param1
and param2
are initialized by AppComponent
, asynchronously, so the components subscribing to both parameters will be "notified" (receive the values) in arbitrary order. getDataFromAPI
should get new data whenever either parameter changes, so both subscriptions call the method, but the other parameter may still be undefined
.
Obviously, this can easily be fixed by simply checking if the other parameter is defined before invoking getDataFromAPI
, but I'm wondering what's the best way to handle this (surely common) scenario? Should I use promises or await to ensure that getDataFromAPI
is only invoked when both (all) parameters are defined?
One simple idea which came to mind is to ensure the ParameterService
only contains a single parameter; i.e. wrap param1
and param2
in some "state class":
export class StateObject {
param1: number;
param2: number;
constructor() { }
}
such that ParameterService
becomes,
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { StateObject } from './stateobject';
@Injectable()
export class ParameterService {
state = new BehaviorSubject(null);
publishState(data: StateObject) {
this.state.next(data);
}
}
You could try:
Or with projection function:
combineLatest API reference