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