Ng2 charts update labels and data

I am trying to dynamically create a graph using ng2-chart, I get information from angular service 2: when I change only the labels of the chart, it works, and when I change the data, only it works, but when I change both of these data, the chart is updated only data. Does anyone have an explanation for this strange behavior.

my template:

<canvas baseChart height="130" width="180" [data]="doughnutChartData" [labels]="doughnutChartLabels" [chartType]="doughnutChartType" (chartHover)="chartHovered($event)" (chartClick)="chartClicked($event)"> </canvas> 

my class:

 export class PlDoughnutComponent implements OnInit { constructor(private homeService: TileServiceService) { } ngOnInit() { this.updatePLdoughnut(); } public util : UtilService = new UtilService(); public doughnutChartLabels:string[] = ['Download Sales']; public doughnutChartData:number[] = [0,0,100]; public doughnutChartType:string = 'doughnut'; public updatePLdoughnut(){ this.homeService.getTile(). then(res => { this.doughnutChartLabels = res.PLtypes; this.doughnutChartData = this.util.objectToIntArray(res.PLByTypes); }) } } 
+17
javascript angular charts ng2-charts
source share
12 answers

Apparently, if you don't change the original reference to the tags array, this seems to work, at least for me. I mean, if you want a completely different set of labels, you should do something like this:

In the template:

 <canvas baseChart [datasets]="lineChartData" [labels]="lineChartLabels" [options]="lineChartOptions" [chartType]="'line'"></canvas> 

In the ts component:

 this.lineChartLabels.length = 0; for (let i = tempLabels.length - 1; i >= 0; i--) { this.lineChartLabels.push(tempLabels[i]); } 

Or using the new ECMAScript syntax:

 this.lineChartLabels.length = 0; this.lineChartLabels.push(...tempLabels); 

The key may be this.lineChartLabels.length = 0; An operator that practically "cleans" your array, setting its length to 0, without changing the link. Hope this helps!

+39
source share

I recently had to use ng2 diagrams, and I had very big problems updating my data until I found this solution:

 <div class="chart"> <canvas baseChart [datasets]="datasets_lines" [labels]="labels_line" [colors]="chartColors" [options]="options" [chartType]="lineChartType"> </canvas> </div> 

and here is what I have in my component:

 import { Component, OnInit, Pipe, ViewChild, ElementRef } from '@angular/core'; import { BaseChartDirective } from 'ng2-charts/ng2-charts'; @Component({ moduleId: module.id, selector: 'product-detail', templateUrl: 'product-detail.component.html' }) export class ProductDetailComponent { @ViewChild(BaseChartDirective) chart: BaseChartDirective; private datasets_lines: { label: string, backgroundColor: string, borderColor: string, data: Array<any> }[] = [ { label: "Quantities", data: Array<any>() } ]; private labels_line = Array<any>(); private options = { scales: { yAxes: [{ ticks: { beginAtZero: true } }] } }; constructor() { } ngOnInit() { this.getStats(); } getStats() { this._statsService.getStatistics(this.startDate, this.endDate, 'comparaison') .subscribe( res => { console.log('getStats success'); this.stats = res; this.labels_line = this.getDates(); this.datasets_lines = []; let arr: any[]; arr = []; for (let stat of this.stats) { arr.push(stat.quantity); } this.datasets_lines.push({ label: 'title', data: arr }); this.refresh_chart(); }, err => { console.log("getStats failed from component"); }, () => { console.log('getStats finished'); }); } refresh_chart() { setTimeout(() => { console.log(this.datasets_lines_copy); console.log(this.datasets_lines); if (this.chart && this.chart.chart && this.chart.chart.config) { this.chart.chart.config.data.labels = this.labels_line; this.chart.chart.config.data.datasets = this.datasets_lines; this.chart.chart.update(); } }); } getDates() { let dateArray: string[] = []; let currentDate: Date = new Date(); currentDate.setTime(this.startDate.getTime()); let pushed: string; for (let i = 1; i < this.daysNum; i++) { pushed = currentDate == null ? '' : this._datePipe.transform(currentDate, 'dd/MM/yyyy'); dateArray.push(pushed); currentDate.setTime(currentDate.getTime() + 24 * 60 * 60 * 1000); } re turn dateArray; } } 

I am sure that this is the right way to do this, and I hope it will be useful

+12
source share

As Dade previously pointed out, this is caused by a combination of Angular 2+ change detection and error in ng2 diagrams.

According to my own observations (correct me if I am wrong), Angular discards several changes in a very short amount of time into one collection ( changes: SimpleChanges ) when ngOnChanges is ngOnChanges .

Unfortunately, ng2 diagrams check to see if the dataset in this collection has been modified and updated it. Otherwise, it completely rebuilds the entire chart. However, due to how change detection works, several properties may have been changed. Then, only the dataset is updated, even if the labels and possibly other properties are updated. See ngOnChanges in ng2 diagrams: valor-software / ng2-charts / src / charts / charts.ts

And if you do not want to have a separate copy of the ng2 diagrams in your application and fix this problem yourself, it may be due to the problem of setting the data set with a small delay using the built-in JavaScript function setTimeout(callback: () => void, delay: number) .

Before:

 @Component({ selector: 'app-root', template: ` <select (change)="onChange($event.target.value)"> <option value="" disabled selected>Select your option</option> <option value="0">Option 0</option> <option value="1">Option 1</option> </select> <canvas baseChart chartType="bar" [datasets]="barChartData" [labels]="barChartLabels" [colors]="barChartColors"> </canvas> ` }) export class AppComponent implements OnInit { chartData: string[]; chartLabels: string[]; chartColors: string[]; onChange(id: string) { getFromApiById(id) .then(result => this._setChart(result.data, result.labels, result.colors)); } private _setChart(data: string[], labels: string[], colors: string[]) { this.chartData = data; this.chartLabels = labels; this.chartColors = colors; } } 

After:

 @Component({ selector: 'app-root', template: ` <select (change)="onChange($event.target.value)"> <option value="" disabled selected>Select your option</option> <option value="0">Option 0</option> <option value="1">Option 1</option> </select> <canvas baseChart chartType="bar" [datasets]="barChartData" [labels]="barChartLabels" [colors]="barChartColors"> </canvas> ` }) export class AppComponent implements OnInit { chartData: string[]; chartLabels: string[]; chartColors: string[]; onChange(id: string) { getFromApiById(id) .then(result => this._setChart(result.data, result.labels, result.colors)); } private _setChart(data: string[], labels: string[], colors: string[]) { this.chartLabels = labels; this.chartColors = colors; setTimeout(() => { this.chartData = data; }, 50); } } 
+9
source share

Using BaseChartDirective, I did a chart update and it served the purpose. Example below:

 import { BaseChartDirective } from 'ng2-charts/ng2-charts'; 

inside the class add below

 @ViewChild(BaseChartDirective) chart: BaseChartDirective; 

As long as you have the values ​​you need to change, add below

 setTimeout(() => { if (this.chart && this.chart.chart && this.chart.chart.config) { this.chart.chart.config.data.labels = this.labels_pie; this.chart.chart.update(); } }); 
+5
source share

The trick is to clear the label and the data array, below the code does not work for me :( `` `

 clearCharts() { this.barChartLabels= []; this.barChartData= [ {data: [], label: 'label1'}, {data: [], label: 'label2'} ]; } 

However when I changed the way I cleared the data helped me (Using object reference)

 clearCharts() { this.barChartLabels= []; this.emptyChartData(this.barChartData); } emptyChartData(obj) { obj[0].data = []; obj[1].data = []; obj[0].label = 'label1'; obj[1].label = 'label2'; } 

`` ``

+4
source share

This is a problem in the ng2-charts library to solve this problem. I cloned github ng2 diagrams in my application directory and completed the following steps:

  • npm install
  • in appmodule import ng-2charts.ts from the src directory.
  • add this updateChartLabels function to the updateChartLabels file
  • call it in the onChanges function.

public ngOnChanges (changes: SimpleChanges): void {if (this.initFlag) {

  if(changes.hasOwnProperty('labels')){ console.log('labels changes ...'); this.updateChartLabels(changes['labels'].currentValue); } //.. //... } private updateChartLabels(newLabelsValues: string[] | any[]): void { this.chart.data.labels = newLabelsValues; } 
+2
source share

This is a problem with the current ng2 diagram library.

Try the new ng4 diagram library that fixed this problem.

https://www.npmjs.com/package/ng4-charts

+2
source share

For those who are looking for a walk, now you can put your labels and data into an object, put this object in an array and just loop over the array in your html. This will redraw the element every time your array changes.

in your script type every time there is a change.

 data = [...]; labels = [...]; chartArray = [{data , labels }] 

in your HTML

 <canvas *ngFor="let chartData of chartArray " [datasets]="chartData.data" [labels]="chartData.labels" > </canvas> 
+2
source share

There is another way:

In your HTML you

 <canvas baseChart [datasets]="ChartData" //...other stuff > </canvas> 

and in the component, I have a function that updates the chart with new data, and then I clone the data sets and rewrite them

 drawChart(){ this.ChartData=[{data: this.dataX, label: 'X'}]; // this.dataX has new values from some place in my code //nothing happened with my chart yet, until I add this lines: let clone = JSON.parse(JSON.stringify(this.ChartData)); this.ChartData=clone; //other stuff like labels etc. } 

it works for me, hope it works for you too.

+1
source share

Since I was not able to get one of the above solutions to work properly, I want to contribute to the solution if someone stumbles on this post and is also stuck with existing approaches.

I have HTML similar to @ mustafa918:

  <div> <canvas #canvas id="canvas" baseChart [datasets]="lineChartData" [labels]="lineChartLabels" [colors]="lineChartColors" [options]="lineChartOptions" [chartType]="lineChartType" [legend]="lineChartLegend" (chartHover)="chartHovered($event)" (chartClick)="chartClicked($event)"> </canvas> </div> 

And to initialize these diagrams in typing, I have:

 public lineChartData: Array<any> = [ { data: this.heights, label: 'Heights History', type: 'line', fill: false}, { data: this.widths, label: 'Widths History', type: 'line', fill: false }]; 

And for me it worked only by setting data and labels at the same time and did not use chart.update () - chart is a reference to BaseChartDirective.

I preloaded the relevant data and labels so that in this.heights, this.width and this.lineChartLabels match the data.

For example: records at heights [i], widths [i] and lineChartLabels [i] coincide with the element in my elementArray at index i => element = {"height": 30, "width": 20, "label": "box "}

 setDatasets() { //store data in chart references var arrHeights = []; for (var i in this.heights) { arrHeights.push({ x: this.lineChartLabels[i], y: this.heights[i] }); } var arrWidths= []; for (var i in this.widths) { arrWidths.push({ x: this.lineChartLabels[i], y: this.widths[i] }); } this.lineChartData[0].data = arrHeights; this.lineChartData[1].data = arrWidths; 

}

Hope this helps someone :) Good luck!

0
source share

Using BaseChartDirective, I did a chart update and it served the purpose. Sample below:

 import { BaseChartDirective } from 'ng2-charts/ng2-charts'; 

inside class add as below

 @ViewChild(BaseChartDirective) chart: BaseChartDirective; 

As long as you have the values ​​you want to change, add as shown below

  this.cart.ngOnChanges({}); 
0
source share

Today I struggled with a similar problem, it seems there is a huge mistake inside the updateChartData function of the ng2-charts library version 1.6.0.

Here is the original function:

  updateChartData = function (newDataValues) { if (Array.isArray(newDataValues[0].data)) { this.chart.data.datasets.forEach(function (dataset, i) { dataset.data = newDataValues[i].data; if (newDataValues[i].label) { dataset.label = newDataValues[i].label; } }); } else { this.chart.data.datasets[0].data = newDataValues; } } 

As you can see, this only updates the data and label, but all other properties are left behind. In my case, I wanted to update pointBorderColor as well, so I decided to override this.

First I get a link to the ng2-charts library:

 import { BaseChartDirective } from 'ng2-charts'; @ViewChild(BaseChartDirective) chart: any; 

It is very important that the type be "any" because otherwise typing will not allow me to redefine a private function.

Then I fix the error in the function and redefine it in afterVIew init:

 ngAfterViewInit(){ if (this.chart) { this.chart.updateChartData = function (newDataValues) { if (Array.isArray(newDataValues[0].data)) { this.chart.data.datasets.forEach(function (dataset, i) { dataset.data = newDataValues[i].data; if (newDataValues[i].pointBorderColor) { dataset.pointBorderColor = newDataValues[i].pointBorderColor; } if (newDataValues[i].label) { dataset.label = newDataValues[i].label; } }); } else { this.chart.data.datasets[0].data = newDataValues; } }.bind(this.chart); } } 
0
source share

All Articles