web-dev-qa-db-fra.com

ng2-charts met à jour les étiquettes et les données

J'essaie de créer dynamiquement un graphique à l'aide de ng2-chart, Je reçois des informations d'un service angulaire 2; lorsque je ne modifie que les étiquettes d'un graphique, cela fonctionne et lorsque je modifie uniquement les données, les données sont mises à jour dans le graphique. avoir quelqu'un une explication pour ce comportement étrange.

mon template:

<canvas baseChart height="130" width="180"

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

ma classe : 

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);

    })
  }
}
14
mustafa918

Apparemment, si vous ne modifiez pas la référence d'origine au tableau labels, cela semble fonctionner, du moins pour moi. Je veux dire, si vous voulez un ensemble d'étiquettes complètement différent, vous devriez faire quelque chose comme ça:

Dans le modèle:

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

Dans la composante ts:

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

Ou, en utilisant la nouvelle syntaxe ECMAScript:

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

La clé est peut-être l'instruction this.lineChartLabels.length = 0;, qui vide pratiquement votre tableau en définissant sa longueur sur 0, sans modifier la référence . J'espère que cela vous aidera!

35
Sleeper9

Récemment, j'ai dû utiliser ng2-charts et j'avais un très gros problème avec la mise à jour de mes données jusqu'à ce que je trouve cette solution:

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

et voici ce que j'ai dans mon composant:

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;
    }    
}

je suis sûr que c'est la bonne façon de le faire et j'espère que cela serait utile.

Comme Deyd l’a déjà souligné, cela est dû à la combinaison de la détection des modifications d’Annular 2 + et d’un bogue dans ng2-charts.

Selon mes propres observations (corrigez-moi si je me trompe), Angular fusionne plusieurs modifications dans un délai très court en une seule collection (changes: SimpleChanges) lorsque ngOnChanges est appelé.

Malheureusement, ng2-charts vérifie uniquement si le dataset a été modifié avec cette collection et le met à jour. Sinon, il reconstruit complètement le graphique entier. Toutefois, en raison du mode de détection des modifications, plusieurs propriétés peuvent avoir été modifiées. Ensuite, seul le jeu de données est mis à jour même si les étiquettes et éventuellement d'autres propriétés ont également été mises à jour. Voir ngOnChanges dans ng2-charts: logiciel-valor/ng2-charts/src/charts/charts.ts

Et si vous ne voulez pas avoir une copie séparée de ng2-charts dans votre application et résoudre le problème vous-même, une solution de contournement possible pour ce problème consiste à définir le jeu de données avec un court délai en utilisant la fonction intégrée setTimeout(callback: () => void, delay: number) de JavaScript.

Avant:

@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;
  }
}

Aprè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);
  }
}
8

À l'aide de BaseChartDirective, j'ai mis à jour le graphique et cela a servi à cela. Échantillon ci-dessous:

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

dans la classe ajouter comme ci-dessous

@ViewChild(BaseChartDirective) chart: BaseChartDirective;

Pendant que vous avez les valeurs à changer, ajoutez comme ci-dessous

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
Chandru

L'astuce consiste à effacer l'étiquette et le tableau de données, le code ci-dessous ne m'a pas aidé:

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';
  }

`` `

3
PraveenIglesias

C'est un problème dans la bibliothèque ng2-charts, pour le résoudre, j'ai cloné le github de ng2-charts dans mon répertoire d'application et j'ai effectué les étapes suivantes: 

  • npm install
  • dans le module d'application, importez ng-2charts.ts à partir du répertoire src.
  • ajouter cette fonction updateChartLabels au fichier chart.ts
  • appelez-le dans la fonction onChanges

public ngOnChanges (changements: 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
mustafa918

Ceci est un problème avec la bibliothèque actuelle ng2-charts.

Essayez la nouvelle bibliothèque ng4-charts qui a résolu ce problème.

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

2
Krishna Modi

Pour ceux qui recherchent une promenade, pour le moment, vous pouvez placer vos étiquettes et vos données dans un objet, le placer dans un tableau et simplement parcourir le tableau dans votre code HTML. Cela redessinra l'élément chaque fois que votre tableau sera modifié.

dans votre script de type chaque fois qu'il y a un changement.

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

dans votre html

<canvas *ngFor="let chartData of chartArray " [datasets]="chartData.data" [labels]="chartData.labels" > </canvas>
1
Hari Masimukkula

Il y a une autre façon de le faire: 

Dans votre HTML, vous avez

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

et dans le composant, j'ai une fonction qui met à jour le graphique avec de nouvelles données, puis je clone les jeux de données et les réaffecte

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.
}

cela fonctionne pour moi, j'espère que cela fonctionne pour vous aussi

1
Ricardo Del Rio

N'ayant pas réussi à faire fonctionner correctement l'une des solutions ci-dessus, je souhaite y apporter ma solution, au cas où quelqu'un tomberait sur ce message et resterait coincé dans les approches actuelles.

J'ai le code HTML similaire à @ 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>

Et pour l’initialisation des données graphiques dans TypeScript, j’ai: 

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

Et pour moi, cela fonctionnait uniquement en définissant les données et les étiquettes en même temps et en n'utilisant pas chart.update () - chart est la référence à BaseChartDirective.

J'ai préalablement chargé les données et les étiquettes respectives, de sorte que, dans this.heights, this.width et this.lineChartLabels soient les données correspondantes.

Par exemple. : Les entrées sur les hauteurs [i], largeurs [i] et lineChartLabels [i] correspondent à l'élément dans mon elementArray à l'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;

}

J'espère que cela aide quelqu'un :) Bonne chance!

0
luminous_koyote