Estoy tratando de crear dinámicamente un gráfico usando ng2-chart, obtengo información de un servicio angular 2, cuando cambio solo las etiquetas del gráfico, funciona y cuando cambio los datos solo funciona, pero cuando cambio ambos solo se actualizan los datos en el gráfico. Que alguien tenga una explicación para este extraño comportamiento.
mi plantilla:
<canvas baseChart height="130" width="180" [data]="doughnutChartData" [labels]="doughnutChartLabels" [chartType]="doughnutChartType" (chartHover)="chartHovered($event)" (chartClick)="chartClicked($event)"> </canvas>mi clase :
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); }) } }Aparentemente, si no modifica la referencia original a la matriz de etiquetas, parece funcionar, al menos para mí. Quiero decir, si desea un conjunto de etiquetas completamente diferente, debe hacer algo como esto:
En la plantilla:
<canvas baseChart [datasets]="lineChartData" [labels]="lineChartLabels" [options]="lineChartOptions" [chartType]="'line'"></canvas>En el componente ts:
this.lineChartLabels.length = 0; for (let i = tempLabels.length - 1; i >= 0; i--) { this.lineChartLabels.push(tempLabels[i]); }O, usando la nueva sintaxis de ECMAScript:
this.lineChartLabels.length = 0; this.lineChartLabels.push(...tempLabels); La clave es quizás this.lineChartLabels.length = 0; declaración, que prácticamente 'vacía' su matriz al establecer su longitud en 0, sin modificar la referencia. ¡Espero que esto ayude!
Recientemente tuve que usar ng2-charts y estaba teniendo muchos problemas para actualizar mis datos hasta que encontré esta solución:
<div class="chart"> <canvas baseChart [datasets]="datasets_lines" [labels]="labels_line" [colors]="chartColors" [options]="options" [chartType]="lineChartType"> </canvas> </div>y aquí lo que tengo en mi componente:
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; } }Estoy seguro de que esta es la forma correcta de hacerlo, y espero que esto sea útil.
Como Deyd señaló antes, esto se debe a una combinación de la detección de cambios de Angular 2+ y un error en los gráficos ng2.
De acuerdo con mis propias observaciones (corríjame si me equivoco), Angular fusiona varios cambios dentro de un período de tiempo muy corto en una sola colección ( changes: SimpleChanges ) cuando se llama ngOnChanges .
Desafortunadamente, ng2-charts solo verifica si el conjunto de datos se ha modificado con esta colección y lo actualiza. De lo contrario, reconstruye completamente todo el gráfico. Sin embargo, debido a la forma en que funciona la detección de cambios, es posible que se haya cambiado más de una propiedad. Luego, solo se actualiza el conjunto de datos, incluso si las etiquetas y posiblemente otras propiedades también se hayan actualizado. Ver ngOnChanges en ng2-charts: valor-software/ng2-charts/src/charts/charts.ts
Y si no desea tener una copia separada de los gráficos ng2 en su aplicación y solucionar el problema usted mismo, una posible solución para este problema es configurar el conjunto de datos con un breve retraso utilizando la función integrada de JavaScript setTimeout(callback: () => void, delay: number) .
Antes:
@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; } }Después:
@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); } }