Ng2-charts: Updating lineChartLabels does not reflect on the chart

Created on 29 Apr 2017  路  18Comments  路  Source: valor-software/ng2-charts

Hi, I'm unable to update labels of a chart. I'm using ng2-charts. On the line chart example here, labels are defined with this line: public lineChartLabels:Array<any> = ['January', 'February', 'March', 'April', 'May', 'June', 'July']; After this line executed I'm changing the value of lineChartLabels with 7 different strings but I can not see any changes on the chart. Do you have any idea?

You can see the problem on this line: https://github.com/mertyildiran/Tower/blob/67d7c7dabfd7a4eafd4202aebdab7ac10cdfce87/src/app/home/home.component.ts#L81

Most helpful comment

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:

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

The key is maybe the this.lineChartLabels.length = 0; statement, which practically 'empties' your array by setting its length to 0, without modifying the reference.
Hope this helps!

All 18 comments

This answer is explaining everything. Here is the summary:

Apparently, Angular2 is unable to detect the label changes so you need to update them manually. First, make sure you have imported ViewChild from @angular/core:

import { Component, OnInit, ViewChild } from '@angular/core';

Then import BaseChartDirective:

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

Get the first chart with (I don't know what to do if you need to handle multiple charts):

export class HomeComponent implements OnInit {
    @ViewChild(BaseChartDirective) chart: BaseChartDirective;
    ...

Lastly, update the labels manually with:

this.chart.chart.config.data.labels = this.lineChartLabels;

Now you should be able to see the new labels on your chart.

I think this issue still needs to fixed.

Getting multiple charts

First import ViewChildren, QueryList and BaseChartDirective with:

import { Component, OnInit, ViewChildren, ElementRef, QueryList, AfterViewInit } from '@angular/core';
import { BaseChartDirective } from 'ng2-charts/ng2-charts';

Now get the all charts available on the page as a QueryList:

export class HomeComponent implements OnInit, AfterViewInit {
    @ViewChildren(BaseChartDirective) charts: QueryList<BaseChartDirective>;

You cannot use charts[0] on QueryLists so you need to iterate using forEach:

    ngAfterViewInit() {
        this.parseCharts();
        this.loadChart1();
        this.loadChart2();
    }

    parseCharts() {
        this.charts.forEach((child) => {
            this.chart.push(child);
        });
        //console.log(this.chart[0]);
    }

ngAfterViewInit() is the most suitable [lifecycle hook] for parsing/extracting charts from the QueryList. constructor() or ngOnInit() is too early to parse them. After parsing you can use the charts with chart[n] respective to DOM order.

How i resolve the problem is like below.

@ViewChildren(BaseChartDirective) charts: QueryList<BaseChartDirective>;

`
this.reportService.getUserSales(startDateString, endDateString).subscribe(t => {

  this.userSaleReponse = t.Data;
  let labels = this.userSaleReponse.map(function (a) { return a.Name; });
  this.userPieLabels = labels;
  let pieData = this.userSaleReponse.map(function (a) { return a.Total; });
  this.userSaleData = pieData;
  this.charts.forEach((child) => {
    if (child.chartType == 'pie') {
      child.labels.splice(0);
      child.labels = labels;
    }
    child.ngOnChanges({} as SimpleChanges);
  });
});

`
I have multiple charts on html side, one is line chart another is pie chart. LineChart works well when I remove the previous array with splice(0) and add items by push. It detects change automatically. But pie chart doesn't work like that. So that I have added QueryList that gets all basechart directives. After the data comes from server, it is organized and then iterates over charts to find the pie chart (which is hacky a bit because if there is more than one pie chart this will fail) and then call the ngOnChanges to invoke detecting changes on piechart.

@kkocabiyik dude why are you putting exactly the same explanation of mine, right after I post? Without even correctly formatting your text... Are you blind?

@mertyildiran Do you know how you'd tell the different charts apart? Lets say you have 5 pie charts, how do you tell which is which? Can you provide them with an identifier in the html template that can be queried later somehow?

@mertyildiran I might add that this is one of the many issues that angular2-chartjs doesn't have. It will update the labels if you simply change the initially provided object.

@mertyildiran I don't see how those lines differentiate them - you are just getting a reference to the list and then iterating through the list treating them all the same.

@jasonburrows oh you are asking about how differentiate this.chart[0] from this.chart[1]. It's simple, it's about the order of the charts inside the DOM. So the script is handling this page:

On that image;

  • this.chart[0] is Traffic vs Average Latency (line chart)
  • this.chart[1] is Message Size vs Response Time (line chart)
  • this.chart[2] is Succesful vs Failed (bar chart)
  • this.chart[3] is browser pie chart
  • this.chart[4] is OS pie chart

@mertyildiran Thanks! Would be nice to be able to identify them with #someChartName but this works.

@jasonburrows You're welcome. Yeah, if you give user the ability to change the order of charts then this approach fails.

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:

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

The key is maybe the this.lineChartLabels.length = 0; statement, which practically 'empties' your array by setting its length to 0, without modifying the reference.
Hope this helps!

I didn't want to create a new bug for this but my issue is a related observation and I don't understand why it works (or doesn't work) this way.
I have 2 small bar charts that are populated from 2 different REST services. I have the service requests set to update the chart data independently of one another in a refreshChart1() and refreshChart2(). I use a drop down that will trigger a the refreshChart calls (picking a timeframe for data). When I use:
this.chart2.ngOnChanges({} as SimpleChanges);
within the refreshChart2(), I notice that the chart 2 data overwrites chart 1 data and I see a reloading of the charts. When I comment out one or the other refreshChart, this does not occur. However when I remove the .ngOnChanges() at the end of each refresh function the graphs are correct. I just don't understand why that is and want to make sure I'm not going to get in trouble down the road. Thanks for any advice!

I have done as Sleeper9 suggested:

  if (this.barChartLabels != null){
    this.barChartLabels.length = 0;
    for (let label of this.priceHistory.barChartLabels){
      this.barChartLabels.push(label);
    }
  }else{
    this.barChartLabels = this.priceHistory.barChartLabels;
  }

Works fine for me. Thanks

@mertyildiran Thanks!! dude, I am working on this bar chart from the last 2 days. Today I got a solution for my chart labels thanks yar you saved me.

@mertyildiran Thanks : )

I believe this issue is now resolved. See the demo app under Pie Chart for dynamically changing labels.

@ViewChildren(BaseChartDirective) charts: QueryList;

Hi

@ViewChildren(BaseChartDirective) charts: QueryList;

for getting the charts and destroying it manually I have used this

It works like a charm, I need to know that whether it is a correct way to do it!

const charts = _.get(this.charts, '_results'); _.forEach(charts, (value) => { if (value) { value.ngOnDestroy(); } });

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hggeorgiev picture hggeorgiev  路  4Comments

SteeledSlagle13 picture SteeledSlagle13  路  3Comments

brandonreid picture brandonreid  路  3Comments

egervari picture egervari  路  4Comments

sarn3792 picture sarn3792  路  4Comments