Ng2-диаграммы обновляют метки и данные

Я пытаюсь динамически создать график, используя 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);

    })
  }
}

Ответ 1

По-видимому, если вы не измените исходную ссылку на массив меток, похоже, это сработает, по крайней мере, для меня. Я имею в виду, если вы хотите совершенно другой набор меток, вы должны сделать что-то вроде этого:

В шаблоне:

<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, без изменения ссылки. Надеюсь это поможет!

Ответ 2

Недавно мне пришлось использовать ng2-диаграммы, и у меня были очень большие проблемы с обновлением моих данных, пока я не нашел это решение:

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

Я уверен, что это правильный способ сделать это, и надеюсь, что это будет полезно

Ответ 3

Как ранее указывал Дейд, это вызвано комбинацией обнаружения изменений Angular 2+ и ошибки в ng2-диаграммах.

Согласно моим собственным наблюдениям (исправьте меня, если я ошибаюсь), Angular сбрасывает несколько изменений за очень короткий промежуток времени в одну коллекцию (changes: SimpleChanges), когда вызывается ngOnChanges.

К сожалению, ng2-диаграммы проверяют, был ли изменен набор данных в этой коллекции и обновил его. В противном случае он полностью перестраивает весь график. Однако из-за того, как работает обнаружение изменений, возможно, было изменено несколько свойств. Затем обновляется только набор данных, даже если обновлены метки и, возможно, другие свойства. См. ngOnChanges в ng2-диаграммах: valor-software/ng2-charts/src/charts/charts.ts

И если вы не хотите иметь отдельную копию ng2-диаграмм в своем приложении и сами исправить эту проблему, возможно, это будет связано с проблемой установки набора данных с небольшой задержкой с помощью встроенной функции 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);
  }
}

Ответ 4

Используя 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();
}
});

Ответ 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';
  }

`` `

Ответ 6

Это проблема в библиотеке ng2-charts, чтобы решить эту проблему. Я клонировал github ng2-диаграмм в моем каталоге приложений и выполнил следующие шаги:

  • npm install
  • в appmodule import ng-2charts.ts из каталога src.
  • добавьте эту функцию updateChartLabels в файл chart.ts
  • вызовите его в функции onChanges.

public ngOnChanges (изменения: 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;
}

Ответ 7

Это проблема с текущей библиотекой ng2-диаграмм.

Попробуйте новую библиотеку ng4-диаграмм, которая исправила эту проблему.

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

Ответ 8

Для тех, кто ищет прогулку, сейчас вы можете поместить свои метки и данные в объект, поместить этот объект в массив и просто перебрать массив в своем html. Это будет перерисовывать элемент каждый раз, когда изменяется ваш массив.

в вашем сценарии типа каждый раз, когда есть изменения.

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

в вашем HTML

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

Ответ 9

Есть и другой способ:

В вашем 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.
}

это работает для меня, надеюсь, что это сработает и для вас.

Ответ 10

Поскольку мне не удалось заставить одно из вышеперечисленных решений работать должным образом, я хочу внести свой вклад в решение, если кто-то наткнется на этот пост и тоже застрянет с существующими подходами.

У меня есть HTML похожий на @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>

И для инициализации данных диаграмм в машинописи у меня есть:

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() - chart является ссылкой на BaseChartDirective.

Я предварительно загрузил соответствующие данные и метки, чтобы в this.heights, this.width и this.lineChartLabels соответствовали данным.

Например: записи на высотах [i], widths [i] и lineChartLabels [i] совпадают с элементом в моем elementArray по индексу я => 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;

}

Надеюсь, это кому-нибудь поможет :) Удачи!

Ответ 11

Используя BaseChartDirective, я сделал обновление диаграммы, и оно послужило цели. Образец ниже:

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

внутри класса добавить, как показано ниже

@ViewChild(BaseChartDirective) chart: BaseChartDirective;

Пока у вас есть значения, которые нужно изменить, добавьте, как показано ниже

 this.cart.ngOnChanges({});

Ответ 12

Сегодня я боролся с подобной проблемой, кажется, есть огромная ошибка внутри функции updateChartData библиотеки ng2-charts версии 1.6.0.

Вот оригинальная функция:

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