I'm pretty new at this and I have read a lot about Observables, EventEmitters, Subject a so on. But it seems I can't find the right way to do this:
I have a parent component which the main app creates various instances of it with *ngFor:
import { DisplayChartService } from 'app/services/display-chart.service';
import { Component, OnInit, Input, Output } from '@angular/core';
import { ActionHolder } from 'app/classes/action-holder';
@Component({
selector: 'dashboardItem',
templateUrl: './dashboard-item.component.html',
styleUrls: ['./dashboard-item.component.scss']
})
export class DashboardItemComponent implements OnInit {
@Input('holder') @Output('holder')
private _holder : ActionHolder;
public get holder() : ActionHolder {
return this._holder;
}
public set holder(v : ActionHolder) {
this._holder = v;
}
setChart(index: number) : void {
let data = this.holder.info.items[index];
this.chartDisplay.setNewData(data);
}
constructor(private chartDisplay: DisplayChartService) {
}
ngOnInit() {
}
}
In the html files, I have some list of buttons which I need to send data through them to the child component threePies:
<div class="card m-0 mt-3">
<div class="card-header">
<h4 class="card-title"> {{ holder.info.actionName }} </h4>
<p class="card-category"> {{ holder.info.date }} </p>
</div>
<div class="card-body pt-0">
<div class="container-fluid m-0 p-0">
<div class="row no-gutters">
<div class="col-6">
<div class="list-group">
<button type="button" class="list-group-item list-group-item-action py-0"
*ngFor="let item of holder.info.items; index as i" (click)="setChart(i)">
{{ item.campaignName }}
</button>
<button type="button" class="list-group-item list-group-item-action list-group-item-secondary py-0">
Total
</button>
</div>
</div>
<div class="col-6">
<threePies [kpis]="holder.data"></threePies>
</div>
</div>
</div>
</div>
</div>
This data should generates a new charts in another child component inside threePies. The problem is: When I'm using a regular observable, only the last generated component remains subscribed to the observable. I've tried Subject and EventEmitter but then all of the components are subscribed together in the same stream with all the buttons from all over the app, affecting all the charts.
What is the best way to handle this? I need to "match" each Observable of dashboardItem to its own child component.
The service:
@Injectable({
providedIn: 'root'
})
export class DisplayChartService {
//... some code that generates the charts...
@Output()
line : EventEmitter<Kpi[]> = new EventEmitter<Kpi[]>();
setNewData(kpis: Kpi[]) {
this.line.emit(kpis);
}
constructor() {
}
}
threePies:
import { Kpi } from '../classes/kpi';
import { Component, OnInit, Output, Input } from '@angular/core';
import { DisplayChartService } from 'app/services/display-chart.service';
@Component({
selector: 'threePies',
templateUrl: './three-pies.component.html',
styleUrls: ['./three-pies.component.scss']
})
export class ThreePiesComponent implements OnInit {
@Input('kpis') @Output('kpis')
private _kpis : Kpi[] = [];
public get kpis() : Kpi[] {
return this._kpis;
}
public set kpis(v : Kpi[]) {
this._kpis = v;
}
@Output('datasets')
private _datasets : any[] = [];
public get datasets() : any[] {
return this._datasets;
}
public set datasets(v : any[]) {
this._datasets = v;
}
constructor(private chartDisplay: DisplayChartService) {
}
ngOnInit() {
for (let i = 0; i < this.kpis.length; i++) {
this.datasets[i] = this.chartDisplay.getDoughuntData(this.kpis[i].kpiData,this.kpis[i].kpiTarget);
}
this.chartDisplay.line.subscribe(newData =>
{
setTimeout(() => {
for (let i = 0; i < newData.length; i++) {
this.datasets[i] = this.chartDisplay.getDoughuntData(newData[i].kpiData,newData[i].kpiTarget);
};
},1000);
console.log(this.datasets);
}
);
}
}