I've been experimenting with ng2-charts and so far I am loving it. I was just wondering what method should be used to add dynamic labels from an API call to the chart?
For example, here is my API call.
this.http
.get('api/v1/data', options)
.map(response => response.json())
.subscribe(
(response) => {
// Init chart data array
let data:Array<any> = new Array();
data[0] = {data: response.events, label: 'Events'};
// Init chart labels
let labels : Array<any> = new Array();
labels[0] = response.labels;
// Set the chart data
this.lineChartData = data;
this.lineChartLabels = labels;
}, (error) => {
console.log(error);
});
It sets the data correctly, but the labels are blank. My response from the API looks like this:
{
"labels": [
"2016-10-04",
"2016-10-03",
"2016-10-02",
"2016-10-01",
"2016-09-30",
"2016-09-29",
"2016-09-28",
"2016-09-27"
],
"events": [ "2", "2", 0, 0, "7", 0, 0, "8"]
}
I initiated the class properties for the chart on the component like this:
public lineChartData:Array<any> = [
{data: [], label: 'Leads'}
];
public lineChartLabels:Array<any> = [];
It appears the labels do not bind to the chart component, but the dataset does.
So I was able to get the chart labels to be placed by doing the following:
In my template I declared the chart as a local variable.
<base-chart #myChart class="chart"
[datasets]="lineChartData"
[labels]="lineChartLabels"
[options]="lineChartOptions"
[colors]="lineChartColors"
[legend]="lineChartLegend"
[chartType]="lineChartType"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)">
</base-chart>
Then grabbed it in my component class.
@ViewChild('myChart') myChart
Then once my API call was complete, I set the data as such
this.http
.get('api/v1/data', options)
.map(response => response.json())
.subscribe(
(response) => {
// Create labels array
let labels : Array<any> = new Array();
labels[0] = response.labels;
// Set the labels
this.myChart.chart.config.data.labels = labels;
}, (error) => {
console.log(error);
});
Not sure if it's the best or proper way, but it does work. This allowed me to access the native chart.js object and just use it's methods and alter it's properties as needed. I'm open for a better solution, but thanks for anyones time!
Hi Anthony,
I have the same issue, yet I do not find the chart property as you do. I also looked into the typescript definition file and it does not define chart as a property. Did you do anything special?
I found another solution to this problem:
@ViewChild( 'goldChart' ) goldChart: BaseChartComponent;
//...
this.goldChart.labels = [/*...*/];
this.goldChart.ngOnChanges({});
@kchat001 where are you importing the BaseChartComponent from in your example above? Can you still achieve this in 1.4.0?
I am experiencing this as well.
@mattblang Have you had any luck in 1.4.0 finding a way around this issue yet?
@anthony-pinskey: I import it the following way:
import {BaseChartComponent} from 'ng2-charts/ng2-charts';
I only used 2.2.1 so far. So, sorry but I can't help you with the older version.
@kchat001 thanks! That makes sense now. The problem was the BaseChartComponent was changed to BaseChartDirective now in 1.4.0 and is causing some issues with how we did it in 1.3.0 and prior.
You have to reference the directive as a ViewChild instead of the component in 1.4.0:
@ViewChild( BaseChartDirective ) chart: BaseChartDirective;
//...
this.chartlabels = [/*...*/];
this.chart.ngOnChanges({});
@Kaizeras @anthony-pinskey @kchat001
I currently have an error because my component is instantiated before I actually receive both the labels and data from an API. How did you guys handle the missing labels when the component is loaded ? Is there anyway of waiting before loading the baseChart directive (1.4.0) ? Thanks !
I would also like to change the labels after filtering the data (some labels may be removed when I filter out some data). I have a data as an @Input in my component and I am using ngOnChanges to transform my data into labels and data.
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
let log: string[] = [];
for (let propName in changes) {
if(propName == "comps") {
let changedProp = changes[propName];
let to = changedProp.currentValue;
if(to != null) {
this.pieChartLabels = to.map(comp => comp.name);
this.pieChartData = to.map(comp => comp.pr_count);
}
}
}
}
Above results in data being updated, but labels being undefined.
@nebril
Two things come to mind :
How did you instantiate your arrays ? Did you try making a shallow copy using .slice() ?
In my case I also had some case of race condition, so I had to update the chart after updating the labels by refering to the answers above.
@Kaizeras @anthony-pinskey @kchat001
I found an answer to my question, by using Resolvers.
I was able to make it work with
@ViewChild(BaseChartDirective) chart: BaseChartDirective;
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
let log: string[] = [];
for (let propName in changes) {
โ if(propName == "comps") {
โ โ let changedProp = changes[propName];
โ โ let to = changedProp.currentValue;
โ โ if(to != null) {
โ โ this.pieChartLabels = to.map(comp => comp.name);
โ โ this.pieChartData = to.map(comp => comp.pr_count);
โ โ //not angulary, hacky, but works
โ โ this.chart.labels = this.pieChartLabels;
โ โ this.chart.data = this.pieChartData;
โ โ this.chart.ngOnChanges({});
โ โ }
โ }
}
}
I know it's really hacky, but at this point I stopped to care as I had to deliver this bloody pie chart.
Thanks for the help all!
@stanislasdrg can you elaborate on your use of Resolvers? You got me interested.
@nebril I referred to this _Resolve: pre-fetching component data_
The idea is to instantiate your component once the data has been fetched.
This should be fixed now from commit #488
This solution @ViewChild(BaseChartDirective) chart: BaseChartDirective; works only if you have one chart on per component. What if we have multiple charts?
@ViewChild('chartName') chart: BaseChartDirective; returning ElementRef on my side and labels are not accessible.
Thanks
I figured it out. In order to use multiple chart we need to include the following markup in our html
and in the component @ViewChild('chartName') chart: BaseChartDirective;
Most helpful comment
This should be fixed now from commit #488