30

我正在尝试使用 ng2-chart 动态创建图表,我从 angular 2 服务获取信息,当我只更改图表的标签时它有效,当我更改数据时只有它有效,但是当我同时更改时,数据都会更新在图表中。有任何人解释这种奇怪的行为。

我的模板:

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

我的课 :

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

    })
  }
}
4

16 回答 16

56

显然,如果您不修改对标签数组的原始引用,它似乎可以工作,至少对我来说是这样。我的意思是,如果你想要一组完全不同的标签,你应该这样做:

在模板中:

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

在 ts 组件中:

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

或者,使用新的 ECMAScript 语法:

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

关键可能是this.lineChartLabels.length = 0;语句,它实际上通过将数组的长度设置为 0 来“清空”你的数组,而不修改引用。希望这可以帮助!

于 2017-06-21T13:06:41.333 回答
17

最近我不得不使用 ng2-charts 并且我在更新数据时遇到了一个非常大的问题,直到我找到了这个解决方案:

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

这里是我的组件中的内容:

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

我确定这是正确的方法,希望这会有所帮助

于 2017-03-09T10:17:40.980 回答
10

就像 Deyd 之前指出的那样,这是由 Angular 2+ 的变更检测和 ng2-charts 中的错误共同造成的。

根据我自己的观察(如果我错了,请纠正我),Angular 在被调用时会在很短的时间内将多个更改合并到一个集合 ( changes: SimpleChanges)ngOnChanges中。

不幸的是,ng2-charts 仅检查数据集是否已随此集合更改并更新它。否则它会完全重建整个图表。但是,由于更改检测的工作方式,可能会更改多个属性。然后,即使标签和可能的其他属性也已更新,也只有数据集得到更新。参见ngOnChangesng2-charts:valor -software/ng2-charts/src/charts/charts.ts

如果您不想在您的应用程序中拥有单独的 ng2-charts 副本并自己解决问题,则可能的解决方法是使用 JavaScript 的内置函数将数据集设置为具有短暂延迟setTimeout(callback: () => void, delay: number)

前:

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

后:

@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);
  }
}
于 2017-05-27T14:33:28.760 回答
8

使用 BaseChartDirective 我做了图表更新,它达到了目的。下面的示例:

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

在类内添加如下

@ViewChild(BaseChartDirective) chart: BaseChartDirective;

当您有要更改的值时,添加如下

setTimeout(() => {
if (this.chart && this.chart.chart && this.chart.chart.config) {
  this.chart.chart.config.data.labels = this.labels_pie;
  this.chart.chart.update();
}
});
于 2017-12-19T12:05:01.243 回答
5

诀窍是清除标签和数据数组,下面的代码对我不起作用:( ```

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

```

于 2017-06-22T16:07:35.803 回答
3

使用 BaseChartDirective 我做了图表更新,它达到了目的。下面的示例:

import { BaseChartDirective } from 'ng2-charts';

在类内添加如下

@ViewChild(BaseChartDirective) chart: BaseChartDirective;

当您有要更改的值时,添加如下

 this.chart.ngOnChanges({});
于 2019-03-13T19:12:26.873 回答
2

这是库 ng2-charts 中的一个问题,为了解决它,我在我的应用程序目录中克隆了 ng2-charts 的 github,并完成了以下步骤:

  • npm install
  • 在 appmodule 中从 src 目录导入 ng-2charts.ts。
  • 将此updateChartLabels函数添加到 chart.ts 文件
  • onChanges函数中调用它。

公共 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;
}
于 2017-03-07T10:53:53.040 回答
2

这是当前 ng2-charts 库的问题。

尝试已修复此问题的新 ng4-charts 库。

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

于 2018-01-02T12:47:29.923 回答
2

对于那些想四处走走的人,现在您可以将标签和数据放入一个对象中,然后将该对象放入一个数组中,然后在 html 中循环遍历该数组。这将在您的数组每次更改时重绘元素。

每次有变化时在你的打字稿中。

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

在你的 html

<canvas *ngFor="let chartData of chartArray " [datasets]="chartData.data" [labels]="chartData.labels" > </canvas>
于 2019-01-14T03:29:56.757 回答
1

还有另一种方法:

在您的 HTML 中,您有

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

在组件中我有一个用新数据更新图表的功能,然后我克隆数据集并重新分配它

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

这对我有用,希望它也对你有用

于 2018-02-09T07:42:16.770 回答
1

我在尝试更新标签时也遇到了这个问题(特别是在尝试发送较短的数组时),这解决了它:

@ViewChild(BaseChartDirective) chart!: BaseChartDirective;

然后,在更新标签时:

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

无需调用 update() 方法。

于 2021-10-18T07:06:06.713 回答
1

我能够通过将处理程序变成箭头函数来解决这个问题

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 ];
  }
}
于 2021-05-13T09:54:21.643 回答
0

对我来说,它只有在使用 ViewChildren 而不是 ViewChild 之后才有效。

TS:

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

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

HTML:

<canvas class="card-line-chart" baseChart #baseLineChart1>
于 2021-11-29T13:23:17.223 回答
0

由于我没有设法使上述解决方案之一正常工作,我想贡献我的解决方案,以防有人偶然发现这篇文章并且也被目前的方法所困扰。

我有类似于@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>

对于打字稿中图表数据的初始化,我有:

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

对我来说,它只能通过同时设置数据和标签而不使用 chart.update() 来工作 - 图表是对 BaseChartDirective 的引用。

我预先加载了各自的数据和标签,这样在this.heights中,this.widththis.lineChartLabels就是对应的数据。

例如: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;

}

我希望这对某人有帮助:)祝你好运!

于 2019-01-16T11:07:14.067 回答
0

今天我遇到了类似的问题,看来 ng2-charts 库版本 1.6.0 的 updateChartData 函数内部存在一个巨大的错误。

这是原始功能:

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

如您所见,这只更新了数据和标签,但所有其他属性都被留下了。就我而言,我还想更新 pointBorderColor 所以我决定覆盖它。

首先,我得到了对 ng2-charts 库的引用:

import { BaseChartDirective } from 'ng2-charts';

@ViewChild(BaseChartDirective) chart: any;

类型是“任何”非常重要,因为否则打字稿将不允许我覆盖私有函数。

然后我修复了函数中的错误并在 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);
    }
}
于 2019-05-16T07:33:22.557 回答
0

基于上述答案,我扩展了此功能,现在一切正常!

TS 代码:

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 代码:

<canvas baseChart [datasets]="lineChartData" class="canvas-wh" [labels]="lineChartLabels"
                                [options]="lineChartOptions" [colors]="lineChartColors" [legend]="lineChartLegend"
                                [chartType]="lineChartType" [plugins]="lineChartPlugins">
于 2021-05-19T13:14:18.103 回答