简体   繁体   English

ng2-charts 更新标签和数据

[英]ng2-charts update labels and data

I'm trying to create dynamically a chart using ng2-chart, I get information from an angular 2 service, when I change only labels of chart it works and when I change data only it works, but When I change both just data are updated in the chart.我正在尝试使用 ng2-chart 动态创建图表,我从 angular 2 服务获取信息,当我仅更改图表标签时,它才有效,而当我更改数据时,它仅有效,但是当我更改两者时,只会更新数据在图表中。 have any one an explication 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);

    })
  }
}

Apparently, if you do not modify the original reference to the labels array, it 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:在 ts 组件中:

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

Or, using new ECMAScript syntax:或者,使用新的 ECMAScript 语法:

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

The key is maybe the this.lineChartLabels.length = 0;关键可能是this.lineChartLabels.length = 0; statement, which practically 'empties' your array by setting its length to 0, without modifying the reference.语句,它实际上通过将数组的长度设置为 0 来“清空”数组,而不修改引用。 Hope this helps!希望这可以帮助!

Recently i had to use ng2-charts and i was having a very big issues with updating my data untill i found this sollution:最近我不得不使用 ng2-charts 并且我在更新我的数据时遇到了一个非常大的问题,直到我找到了这个解决方案:

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

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

im sure this is the right way to do it, and hope this would be helpfull我确定这是正确的方法,希望这会有所帮助

Like Deyd pointed out before, this is caused by a combination of Angular 2+'s change detection and a bug in ng2-charts.就像 Deyd 之前指出的那样,这是由 Angular 2+ 的变化检测和 ng2-charts 中的错误组合引起的。

According to my own observations (correct me if I'm wrong), Angular merges several changes within a very short timeframe into a single collection ( changes: SimpleChanges ) when ngOnChanges is called.根据我自己的观察(如果我错了,请纠正我),当ngOnChanges时,Angular 会在很短的时间内将多个更改合并到一个集合中( changes: SimpleChanges )。

Unfortunately, ng2-charts only checks if the dataset has been changed with this collection and updates it.不幸的是,ng2-charts 只检查数据集是否已随此集合更改并更新它。 Otherwise it completely rebuilds the entire chart.否则,它会完全重建整个图表。 However, because of the way the change detection works, more than one property might have been changed.但是,由于更改检测的工作方式,可能已更改多个属性。 Then, only the dataset gets updated even if the labels and possibly other properties have been updated as well.然后,即使标签和可能的其他属性也已更新,也只会更新数据集。 See ngOnChanges in ng2-charts: valor-software/ng2-charts/src/charts/charts.ts参见ngOnChanges -charts 中的 ngOnChanges:valor- software/ng2-charts/src/charts/charts.ts

And if you don't want to have a separate copy of ng2-charts in your app and fix the problem yourself, a possible workaround for this problem is to set the dataset with a short delay using JavaScript's built-in function setTimeout(callback: () => void, delay: number) .如果你不想在你的应用程序中有一个单独的 ng2-charts 副本并自己解决这个问题,这个问题的一个可能的解决方法是使用 JavaScript 的内置函数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);
  }
}

Using BaseChartDirective i did chart update and it served the purpose.使用 BaseChartDirective 我做了图表更新,它达到了目的。 Sample below:示例如下:

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

inside the class add as below在类中添加如下

@ViewChild(BaseChartDirective) chart: BaseChartDirective;

While you have the values to be changed, add as 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();
}
});

The trick is in clearing the label and data array, the below code didnt 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';
  }

``` ``

This is an issue in the library ng2-charts, to resolve it I have cloned the github of ng2-charts in my app directory and have done following steps :这是库 ng2-charts 中的一个问题,为了解决它,我在我的应用程序目录中克隆了 ng2-charts 的 github 并完成了以下步骤:

  • npm install
  • in appmodule import ng-2charts.ts from src directory.在 appmodule 中从 src 目录导入 ng-2charts.ts。
  • add this updateChartLabels function to chart.ts file将此updateChartLabels函数添加到 chart.ts 文件
  • call it in the onChanges function.onChanges函数中调用它。

public ngOnChanges(changes: SimpleChanges): void { if (this.initFlag) { 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;
}

This is an issue with the current ng2-charts library.这是当前 ng2-charts 库的一个问题。

Try the new ng4-charts library which has fixed this issue.尝试解决此问题的新 ng4-charts 库。

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

For those looking for a walk around, for now you can put your labels and data in an object and put that object in an array and just loop through the array in your html.对于那些想四处走走的人,现在您可以将标签和数据放入一个对象中,然后将该对象放入一个数组中,然后在 html 中循环遍历该数组。 This will redraw the element every time your array changes.每次数组更改时,这都会重绘元素。

in your type script every time there's a change.每次发生变化时都在您的类型脚本中。

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

in your html在你的 html

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

There is another way to do it:还有另一种方法可以做到:

In your HTML you have在您的 HTML 中,您有

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

and in the component I have a function which update the chart with new data, and then I clone the datasets and re-assign it在组件中,我有一个用新数据更新图表的函数,然后我克隆数据集并重新分配它

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

this works for me, hope it works for you too这对我有用,希望它也对你有用

Since I didn't manage to get one of the above solutions to work properly, I want to contribute my solution, in case someone stumbles across this post and also got stuck with the present approaches.由于我没有设法使上述解决方案之一正常工作,我想贡献我的解决方案,以防有人偶然发现这篇文章并且也被当前的方法卡住了。

I have the HTML similar to @mustafa918:我有类似于@mustafa918 的 HTML:

 <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 for the initialisation of the charts data in typescript 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 the data and labels at the same time and don't use chart.update() - chart is the reference to the BaseChartDirective.对我来说,它仅通过同时设置数据和标签起作用,并且不使用 chart.update() - chart 是对 BaseChartDirective 的引用。

I loaded the respective data and labels beforehand, so that in this.heights, this.width and this.lineChartLabels are corresponding data.我预先加载了各自的数据和标签,所以在this.heights、this.widththis.lineChartLabels中是对应的数据。

Eg : The entries on heights[i], widths[i] and lineChartLabels[i] match with the element in my elementArray at index i => element ={ "height":30, "width":20, "label":"box"}例如: heights[i]、widths[i] 和 lineChartLabels[i] 上的条目与我elementArray中索引 i => element ={ "height":30, "width":20, "label" 中的元素匹配: “盒子”}

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;

} }

I hope this helps someone :) Good Luck!我希望这对某人有所帮助:) 祝你好运!

Using BaseChartDirective i did chart update and it served the purpose.使用 BaseChartDirective 我做了图表更新,它达到了目的。 Sample below:示例如下:

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

inside the class add as below在类中添加如下

@ViewChild(BaseChartDirective) chart: BaseChartDirective;

While you have the values to be changed, add as below当您要更改值时,添加如下

 this.chart.ngOnChanges({});

Today i struggled with similar problem, it appears there is a huge bug inside the updateChartData function of ng2-charts library version 1.6.0.今天我遇到了类似的问题,似乎在 ng2-charts 库版本 1.6.0 的 updateChartData 函数中存在一个巨大的错误。

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 updates only the data and the label, but all other properties are left behind.如您所见,这仅更新数据和标签,但保留了所有其他属性。 In my case i wanted to update also the pointBorderColor so i decided to override this.在我的情况下,我也想更新 pointBorderColor 所以我决定覆盖它。

First i get a reference to the ng2-charts library:首先,我得到了对 ng2-charts 库的引用:

import { BaseChartDirective } from 'ng2-charts';

@ViewChild(BaseChartDirective) chart: any;

It is very important the type is "any", because otherwise typescript will not allow me to override a private function.类型是“任何”非常重要,否则打字稿将不允许我覆盖私有函数。

Then i fix the bug in the function and override it in afterVIew init:然后我修复函数中的错误并在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);
    }
}

I was able to fix this issue by turning the handler into an arrow function我能够通过将处理程序转换为箭头函数来解决此问题

export class HistogramChartComponent implements OnInit {

  constructor(private dataService: MyFruitService ) { }
  
  barChartOptions: ChartOptions = { responsive: true };
  barChartLabels: Label[] = ['Apple', 'Banana', 'Kiwifruit', 'Blueberry', 'Orange', 'Grapes'];
  barChartType: ChartType = 'bar';
  barChartLegend = true;
  barChartPlugins = [];

  barChartData: ChartDataSets[] = [
    { data: [45, 37, 60, 70, 46, 33], label: 'Best Fruits' }
  ];

  ngOnInit() {
    this.dataService
      .getDocument("Foobar")
      .subscribe(this.handleResponse);
  }

  handleResponse = (doc: MyFruitDocument) => {
      console.log('document: ', doc);
      
      let labels = doc.dataPoints.map(p => p.fruitName);
      let data = { data: doc.dataPoints.map(p => p.value), label: 'Best Fruits' };

      this.barChartLabels = labels;    
      this.barChartData = [ data ];
  }
}

Based on above answers, I extended this function and everything works fine now!根据上述答案,我扩展了此功能,现在一切正常!

TS Code:代码:

Declare : import { BaseChartDirective } from 'ng2-charts';

          @ViewChild(BaseChartDirective) chart: BaseChartDirective;

           public lineChartData: ChartDataSets[] = [
               { data: [0, 0, 0, 0, 0, 0, 0], label: 'Data 1' },
               { data: [0, 0, 0, 0, 0, 0, 0], label: 'Data 2' }
             ];

           public lineChartLabels: Label[] = ['Label1', 'Label2', 'Label3', 'Label4', 
                  'Label5', 'Label6';

TS Function:

    refresh_chart(){
        setTimeout(() => {
          if (this.chart && this.chart.chart && this.chart.chart.config) {
            this.chart.chart.config.data.datasets.forEach(x => {
              x.data = [];
            });
            let index = 0;
              this.chart.chart.config.data.datasets.forEach(x => {
                x.data = this.lineChartData[index].data;
                index++;
              });
              this.chart.chart.update();
          }
      }, 500);
      }

HTML Code: HTML代码:

<canvas baseChart [datasets]="lineChartData" class="canvas-wh" [labels]="lineChartLabels"
                                [options]="lineChartOptions" [colors]="lineChartColors" [legend]="lineChartLegend"
                                [chartType]="lineChartType" [plugins]="lineChartPlugins">

I have also faced this issue when trying to update the labels, (specifically when trying to send a shorter array), And this solved it:我在尝试更新标签时也遇到了这个问题(特别是在尝试发送较短的数组时),这解决了它:

@ViewChild(BaseChartDirective) chart!: BaseChartDirective;

and then, when updating the labels:然后,在更新标签时:

this.chart.chart!.config.data.labels = [...]

No need to call the update() method.无需调用 update() 方法。

For me, it worked only after using ViewChildren and not ViewChild.对我来说,它只有在使用 ViewChildren 而不是 ViewChild 后才有效。

TS: TS:

@ViewChildren('baseLineChart1') chart !: QueryList<BaseChartDirective>;

this.chart.forEach((child) => { child.update() })

HTML: HTML:

<canvas class="card-line-chart" baseChart #baseLineChart1>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM