I am trying to update the colors of the dougnut/piechart when the data changes. (When my data changes, there could be less/more sections of the chart and each piece of data comes with a hex background color.) I have initiated the colors two different ways successfully but when I change the data & colors, only the data changes and not the colors. Here are the two different ways I initiate the colors:
1)
public pieChartColors: any[] = [{
backgroundColor: []
}]
for(let r of this.records){
this.pieChartColors[0].backgroundColor.push(r.bgColor);
}
<canvas baseChart
[datasets]="pieChartData"
[labels]="pieChartLabels"
[colors]="pieChartColors"
[chartType]="pieChartType"></canvas>
The problem with this solution is clearing the pieChartColors array. The solution above works until I add this above the for loop:
this.pieChartColors[0].backgroundColor = [];
Then the colors don't get initialized / updated at all. I am getting this error as well:
Error in http://localhost:3000/app/dashboard/sales-pipeline/sales-pipeline.component.html:11:20 caused by: Cannot read property 'custom' of undefined
2)
I do everything the same as above except I include the backgroundColor inside of the data array like so:
public pieChartData:any[] = [{
data: [],
backgroundColor: []
}];
and I initiate colors with an empty array, but the colors still get pulled from the data array:
colorsEmptyObject: Array<any> = [{}];
<canvas baseChart
[datasets]="pieChartData"
[labels]="pieChartLabels"
[colors]="colorsEmptyObject"
[chartType]="pieChartType"></canvas>
This solution has the same problem above where if I clear the array before assigning the colors, then the colors never get initialized/updated. Also, in both scenarios the data gets updated successfully even though the colors do not. The error that I am receiving occurs in both scenarios.
I referred to this issue (https://github.com/valor-software/ng2-charts/issues/251) to try and work this out. Others pointed out and I agree that the documentation should be updated to include how to initialize the piechart/doughnut colors because it is different than the line/bar charts that I used.
Updating the data only triggers the chartjs internal update() method and as you can see here: http://www.chartjs.org/docs/#scales-update-default-scale-config this only will update data related objects.
I need to do the same thing as you but i'm afraid that currently is not possible since the only way the whole chart gets refreshed is when it loads the first time: https://github.com/valor-software/ng2-charts/blob/development/src/charts/charts.ts#L78
Anyone have a workaround for this?
I'm going to answer my own question and here is a workaround:
ng2-charts/charts/chart.js, method ngOnChanges line 21:
BaseChartDirective.prototype.ngOnChanges = function (changes) {
if (this.initFlag) {
// Check if the changes are in the data or datasets
if (changes.hasOwnProperty('labels') || changes.hasOwnProperty('data') || changes.hasOwnProperty('datasets')) {
var refresh = false;
if (changes['data']) {
refresh = this.updateChartData(changes['data'].currentValue);
}
else {
refresh = this.updateChartData(changes['datasets'].currentValue);
}
if(refresh) {
this.refresh();
} else {
this.chart.update();
}
}
else {
// otherwise rebuild the chart
this.refresh();
}
}
};
I've added a refresh flag, and depending on it's value force to refresh the whole chart or only the data. Also I've changed the updateChartData() method so it returns true or false:
BaseChartDirective.prototype.updateChartData = function (newDataValues) {
if (Array.isArray(newDataValues[0].data)) {
if(this.chart.data.datasets.length == newDataValues.length) {
this.chart.data.datasets.forEach(function (dataset, i) {
dataset.data = newDataValues[i].data;
if (newDataValues[i].label) {
dataset.label = newDataValues[i].label;
}
});
} else {
var newChartData = [];
newDataValues.forEach(function (dataset, i) {
var serie = {};
serie.data = dataset.data;
if (dataset.label) {
serie.label = dataset.label;
}
newChartData.push(serie);
});
this.chart.data.datasets = newChartData;
return true;
}
}
else {
if(this.chart.data.datasets[0].data.length != newDataValues.length) {
return true;
}
this.chart.data.datasets[0].data = newDataValues;
}
return false;
};
Hope this help.
Regards
Pending the fix, show @ryan-morris solution #547 (get access to BaseChartDirective and do refresh when you want)
So I have been trying to figure this out forever now and I am able to get everything to update with the code I have below. Sorry I am pulling the code snippets directly out of my source (I hope it still make sense), but I am not sure why it works versus all of the other attempts I made.
For context, I am displaying a pie chart of the bid distribution for an individual auction. Each pie piece of data represents the total number of bids that a team made on that auction.
My auction component that has the data for the pie chart:
The corresponding properties.
I first populate the properties w/ default settings.
public bidDistributionStatLabels: string[] = ['Download Sales', 'In-Store Sales', 'Mail Sales'];
public bidDistributionStatColors: {}[] = [ { backgroundColor: ['red', 'orange', 'green'] } ];
public bidDistributionStatData: number[] = [300, 500, 100];
public bidDistributionStatType: string = 'pie';
public bidDistributionStatOptions: { animation } = { animation: false };
Some methods to update the chart's data (my real data bid data) when I click on it. Note that I must set the length of all arrays to 0 for data, labels, and colors before updating (this is key).
/**
* Update the bid_distribution stats to reflect auction's present state.
* @returns void
*/
protected updateBidDistributionStat()
{
this.bidDistributionStatLabels.length = 0;
this.bidDistributionStatData.length = 0;
this.bidDistributionStatColors.length = 0;
var chartObject = {};
// Build Chart Object Labels/Data.
this.auction.getBids().forEach(function(bidObject)
{
if(typeof chartObject[bidObject.getTeam().getAbbreviation()] === 'undefined')
{
chartObject[bidObject.getTeam().getAbbreviation()] = {};
chartObject[bidObject.getTeam().getAbbreviation()].value = 0;
chartObject[bidObject.getTeam().getAbbreviation()].color = bidObject.getTeam().getPrimaryColor();
}
chartObject[bidObject.getTeam().getAbbreviation()].value++;
});
this.bidDistributionStatLabels = Object.keys(chartObject);
// Init colors
var colorObject = {
backgroundColor: []
};
for (var team in chartObject)
{
this.bidDistributionStatData.push(chartObject[team].value);
colorObject.backgroundColor.push(chartObject[team].color);
}
this.bidDistributionStatColors.push(colorObject);
}
chartClicked(e: any): void
{
console.log('chart clicked!');
this.updateBidDistributionStat();
}
The corresponding template/html:
<div style="display: block">
<canvas baseChart
[data]="bidDistributionStatData"
[labels]="bidDistributionStatLabels"
[chartType]="bidDistributionStatType"
[options]="bidDistributionStatOptions"
[colors]="bidDistributionStatColors"
(chartHover)="chartHovered($event)"
(chartClick)="chartClicked($event)"></canvas>
</div>
Notes/Comments:
I have tried a TON of different variations of this, but this appears to be the only way to get the chart to update with new colors, data, and labels from the data derived during my each loop.
Closing for inactivity - if issue persists, feel free to comment with a working example (codepen/plunkr/...) which demonstrates the issue.
Most helpful comment
I'm going to answer my own question and here is a workaround:
ng2-charts/charts/chart.js, method ngOnChanges line 21:
I've added a refresh flag, and depending on it's value force to refresh the whole chart or only the data. Also I've changed the updateChartData() method so it returns true or false:
Hope this help.
Regards