facebook-squared twitter-squared rss-squared gplus-squared

Add Dynamic data to ng2-chart

Written by on 10 July 2018

Tuesday

10

July 2018

2

COMMENTS

Previously on this blog.

Person A: I need a chart on my app, which looks like a circle. A circle without the center.

Person B: A what, you mean pie.

Person A: Not a pie it has it’s name.

Person B: “What name”

Person A: It start with d, da, a 

Person B:doughnut chart

Person A: Yes, a dougnut chart

Person B: Then visit back and you have all the information you need

After an hour or so

Person A: But it doesn’t work with dynamic data

Person B: Don’t worry that’s what we gonna cover in this article.

Conversation ends.

After adding doughnut chart to my Ionic Application, I have realized I needed to power it up with dynamic data. Obviously how much is the case that we feed static data to populate the chart. Normally we have large amount of data and charts help us to easily visualize them. 

Setting up environment for dynamic data

Jsonvat returns VAT for different european countries. The purpose of this API is to have real data not just a test data. The response looks like

A image of code snippet which show how VAT of EU nations looks in JSON format

I know, this is not world best statistics to be shown in doughnut chart.  BTW we need to only extract the name and standard rate for chart label and chartData. Let’s use manipulate the rates array to our likings.

import { HttpClient } from "@angular/common/http";
import { pipe } from "rxjs";
import { map, take } from "rxjs/operators";

public doughnutChartLabels:string[] = [];
public doughnutChartData:number[] = [];
public doughnutChartType:string = 'doughnut';

constructor(public navCtrl: NavController, private http: HttpClient){
  this.http.get('https://jsonvat.com/').pipe(map((resp:any) => 
    resp.rates.slice(0,5) )).subscribe(resp => {
      resp.forEach(item => {
        this.doughnutChartLabels.push(item.name);
        this.doughnutChartData.push(item.periods[0].rates.standard)
    })
  })
}

On line 11 we are only interested in rates and we don’t need whole lot of data, so let’s stick with five results. Next we loop through the array and push the name and data into corresponding arrays.

Result

Nothing, simply nothing. The canvas received no data so nothing rendered on the screen. Actually canvas is already drawn by the time data is received from the jsonvat.com server. We have some solution up our sleeves. Let go over them one by one.

Suggested Solution 1:

The most obvious solution in angular era is to use *ngIf* directive whose job is to not render the canvas until the data is arrived.

showChart: boolean = false;
public doughnutChartLabels:string[] = [];
....

constructor(..
  resp.forEach(item => {
      ...
   })
  this.showChart = true;
})

Suggested Solution 1 Result:

Nothing, I don’t know what happens here but for some reason canvas is not getting any immediate set of data, henceforth empty chart.

Suggestion Solution 2:

If you go through this thread, there are many solutions. I don’t want to get into the details as none worked for me. 

I was stuck at this point for quite a while. I found my solution after many trial and errors. I don’t want you to spend time in uncertainty. That why I wanted to share it with you.

Note: This might not be a perfect solution.

Working Solution

The only solution that worked for is using dynamic components.  All we have to do is move all the code to separate component called doughnut. Leave the data fetching logic in parent component. Than dynamically load the doughnut component and provide it the necessary data so that doughnut chart is properly populated.

The final parent component now looks like

<div #container></div>
import { ...ViewContainerRef, ComponentFactoryResolver } from '@angular/core';

@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;

constructor(
...
private resolver: ComponentFactoryResolver
){
  ....
  const factory = this.resolver.resolveComponentFactory(PieComponent);
  const componentRef = this.container.createComponent(factory);    

  componentRef.instance.doughnutChartData = chartData;
  componentRef.instance.doughnutChartLabels = chartLabels;
}

Line 3: We are converting a #container div into proper angular container

Line 10-11:  We are using factory resolver which using behind the scenes angular magic to create and insert our doughnut component inside the container.

Line 13-14: Provide the input data to the dynamically created component.

Final listing for child component is 

<canvas baseChart
    [data]="doughnutChartData" 
    [options]="donutOptions"
    [labels]="doughnutChartLabels" 
    [chartType]="doughnutChartType" 
  ></canvas>
import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-doughnut',
  templateUrl: './doughnut.component.html',
})

export class DoughnutComponent {

  @Input('doughnutChartLabels') doughnutChartLabels: string[];
  @Input('doughnutChartData') doughnutChartData: number[];
  public doughnutChartType: string = 'doughnut';
  constructor() {}

}

Everything is straightforward here apart from some input properties.

Final Output

An Image of doughnut chart that show the data of VAT for different EU states

Summary

Most of the Async view or template that is dependant on the request from the server can easily be handled with *ngIf directive. Sometimes a package will have some sort of events or hooks. In any case we have dynamic components so why not use.

Leave your comments down below and tell me what do you think about this article.

2 Comments

  1. Philipp

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: