Ng2-charts: Dynamically Generated Labels from API Call?

Created on 4 Oct 2016  ยท  17Comments  ยท  Source: valor-software/ng2-charts

I've been experimenting with ng2-charts and so far I am loving it. I was just wondering what method should be used to add dynamic labels from an API call to the chart?
For example, here is my API call.

            this.http
              .get('api/v1/data', options)
              .map(response => response.json())
              .subscribe(
                  (response) => {
                    // Init chart data array
                    let data:Array<any> = new Array();
                    data[0] = {data: response.events, label: 'Events'};
                    // Init chart labels
                    let labels : Array<any> = new Array();
                    labels[0] = response.labels;
                    // Set the chart data
                    this.lineChartData   = data;
                    this.lineChartLabels = labels;

              }, (error) => {
                    console.log(error);
            });

It sets the data correctly, but the labels are blank. My response from the API looks like this:

{
  "labels": [
    "2016-10-04",
    "2016-10-03",
    "2016-10-02",
    "2016-10-01",
    "2016-09-30",
    "2016-09-29",
    "2016-09-28",
    "2016-09-27"
  ],
  "events": [ "2", "2", 0, 0, "7", 0, 0, "8"]
}

I initiated the class properties for the chart on the component like this:

         public lineChartData:Array<any> = [
           {data: [], label: 'Leads'}
         ];

         public lineChartLabels:Array<any> = [];

It appears the labels do not bind to the chart component, but the dataset does.

Most helpful comment

This should be fixed now from commit #488

All 17 comments

So I was able to get the chart labels to be placed by doing the following:

In my template I declared the chart as a local variable.

                    <base-chart #myChart class="chart"
                        [datasets]="lineChartData"
                        [labels]="lineChartLabels"
                        [options]="lineChartOptions"
                        [colors]="lineChartColors"
                        [legend]="lineChartLegend"
                        [chartType]="lineChartType"
                        (chartHover)="chartHovered($event)"
                        (chartClick)="chartClicked($event)">
                    </base-chart>

Then grabbed it in my component class.
@ViewChild('myChart') myChart

Then once my API call was complete, I set the data as such

            this.http
              .get('api/v1/data', options)
              .map(response => response.json())
              .subscribe(
                  (response) => {
                    // Create labels array
                    let labels : Array<any> = new Array();
                    labels[0] = response.labels;
                   // Set the labels
                   this.myChart.chart.config.data.labels = labels;

              }, (error) => {
                    console.log(error);
            });

Not sure if it's the best or proper way, but it does work. This allowed me to access the native chart.js object and just use it's methods and alter it's properties as needed. I'm open for a better solution, but thanks for anyones time!

Hi Anthony,

I have the same issue, yet I do not find the chart property as you do. I also looked into the typescript definition file and it does not define chart as a property. Did you do anything special?

I found another solution to this problem:

@ViewChild( 'goldChart' ) goldChart: BaseChartComponent;
//...
this.goldChart.labels = [/*...*/];
this.goldChart.ngOnChanges({});

@kchat001 where are you importing the BaseChartComponent from in your example above? Can you still achieve this in 1.4.0?

I am experiencing this as well.

@mattblang Have you had any luck in 1.4.0 finding a way around this issue yet?

@anthony-pinskey: I import it the following way:
import {BaseChartComponent} from 'ng2-charts/ng2-charts';

I only used 2.2.1 so far. So, sorry but I can't help you with the older version.

@kchat001 thanks! That makes sense now. The problem was the BaseChartComponent was changed to BaseChartDirective now in 1.4.0 and is causing some issues with how we did it in 1.3.0 and prior.

You have to reference the directive as a ViewChild instead of the component in 1.4.0:

@ViewChild( BaseChartDirective ) chart: BaseChartDirective;

//...

this.chartlabels = [/*...*/];
this.chart.ngOnChanges({});

@Kaizeras @anthony-pinskey @kchat001
I currently have an error because my component is instantiated before I actually receive both the labels and data from an API. How did you guys handle the missing labels when the component is loaded ? Is there anyway of waiting before loading the baseChart directive (1.4.0) ? Thanks !

I would also like to change the labels after filtering the data (some labels may be removed when I filter out some data). I have a data as an @Input in my component and I am using ngOnChanges to transform my data into labels and data.

ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
  let log: string[] = [];
  for (let propName in changes) {
    if(propName == "comps") {
         let changedProp = changes[propName];
         let to = changedProp.currentValue;

          if(to != null) {
             this.pieChartLabels = to.map(comp => comp.name);
             this.pieChartData = to.map(comp => comp.pr_count);
          }
    }
  }
}

Above results in data being updated, but labels being undefined.

@nebril
Two things come to mind :
How did you instantiate your arrays ? Did you try making a shallow copy using .slice() ?
In my case I also had some case of race condition, so I had to update the chart after updating the labels by refering to the answers above.

@Kaizeras @anthony-pinskey @kchat001
I found an answer to my question, by using Resolvers.

I was able to make it work with

@ViewChild(BaseChartDirective) chart: BaseChartDirective;

ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
  let log: string[] = [];
  for (let propName in changes) {
  โ”† if(propName == "comps") {
  โ”†   โ”† let changedProp = changes[propName];
  โ”†   โ”† let to = changedProp.currentValue;

  โ”†   โ”† if(to != null) {
  โ”†   โ”†   this.pieChartLabels = to.map(comp => comp.name);
  โ”†   โ”†   this.pieChartData = to.map(comp => comp.pr_count);
  โ”†   โ”†   //not angulary, hacky, but works
  โ”†   โ”†   this.chart.labels = this.pieChartLabels;
  โ”†   โ”†   this.chart.data = this.pieChartData;
  โ”†   โ”†   this.chart.ngOnChanges({});
  โ”†   โ”† }
  โ”† }
  }
}

I know it's really hacky, but at this point I stopped to care as I had to deliver this bloody pie chart.

Thanks for the help all!

@stanislasdrg can you elaborate on your use of Resolvers? You got me interested.

@nebril I referred to this _Resolve: pre-fetching component data_
The idea is to instantiate your component once the data has been fetched.

This should be fixed now from commit #488

This solution @ViewChild(BaseChartDirective) chart: BaseChartDirective; works only if you have one chart on per component. What if we have multiple charts?

@ViewChild('chartName') chart: BaseChartDirective; returning ElementRef on my side and labels are not accessible.

Thanks

I figured it out. In order to use multiple chart we need to include the following markup in our html

and in the component @ViewChild('chartName') chart: BaseChartDirective;

Was this page helpful?
0 / 5 - 0 ratings

Related issues

brandonreid picture brandonreid  ยท  3Comments

Maistho picture Maistho  ยท  3Comments

egervari picture egervari  ยท  4Comments

RKornu picture RKornu  ยท  3Comments

sarn3792 picture sarn3792  ยท  4Comments