Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
132 lines (100 sloc) 6.26 KB
layout permalink title path lang ref tags
post
/dynamic-charts-in-angular-2-with-highcharts
Create dynamic charts with Angular 2 and Highcharts
2017-01-20-dynamic-charts-in-angular-2-with-highcharts.md
en
dynamic-charts-in-angular-2-with-highcharts
Angular 2
Highcharts

In this article I'm using Angular 2 with TypeScript and ES5 so if you don't understand what it is, I recommend you this John Papa awesome article. I'm also using SystemJS and not Webpack. If you don't know what SystemJS and Webpack means, follow the previous links. To my mind it's easier to work with SystemJS but it's you to choose which one you want to use.

Our starting point will be the below Plunker which is actually a simple Highcharts instance wrapped into an Angular 2 component. (I'll write an article about that later!)

<iframe style="width: 100%; height: 520px" src="//embed.plnkr.co/0qicjW?show=preview" frameborder="0" allowfullscren="allowfullscren"></iframe>

All the following code comes from the app.component.ts file (in the above Plunker example).

{% highlight javascript %} @Component({ selector: 'my-app', template: <div><div #chart></div></div> }) {% endhighlight %}

This piece of code is the declaration of the Angular 2 component. It is as simple as that! It is only needed to define the selector to bootstrap Angular 2 on it and our template to create our chart.

<div #chart></div> is a special element in order to allow us to inject data inside or manipulate it later inside the Class or the constructor.

After the component declaration, we can create the component's Class with some dependencies: {% highlight javascript %} export class AppComponent implements AfterViewInit, OnDestroy {} {% endhighlight %}

And here comes the rest of the code into the Class: {% highlight javascript %} @ViewChild('chart') public chartEl: ElementRef;

private _chart: any;

private randomValue() { return Math.floor(Math.random() * 10) + 0; }

public ngAfterViewInit() { let opts: any = { xAxis: { type: 'datetime', tickPixelInterval: 150 }, series: [{ name: 'Random data', data: (function () { var data = [], time = (new Date()).getTime(), i;

          for (i = -19; i <= 0; i += 1) {
              data.push({
                  x: time + i * 1000,
                  y: Math.floor(Math.random() * 10) + 0
              });
          }
          return data;
      }())
    }]
};

if (this.chartEl && this.chartEl.nativeElement) {
    opts.chart = {
        type: 'spline',
        renderTo: this.chartEl.nativeElement
    };

    this._chart = new Highcharts.Chart(opts);
}

}

public ngOnDestroy() { this._chart.destroy(); } {% endhighlight %} I will not explain all the above code but only the important parts.

So here we set the @ViewChild('chart') decorator to access a DOM element in the same way as angular.element() in Angular 1.x, here we want to manipulate the <div #chart></div> element.

After that we create a private _chart: any; variable which will contain the chart instance.

{% highlight javascript %} if (this.chartEl && this.chartEl.nativeElement) { opts.chart = { type: 'spline', renderTo: this.chartEl.nativeElement };

this._chart = new Highcharts.Chart(opts);

} {% endhighlight %}

When the Angular view is initialized (ngAfterViewInit()), the above code verifies the chart DOM element is ready and finally adds to the chart options object (opts) its type and the rendering element reference we mentioned above.

After that we can create a new Highcharts instance stored in _chart variable.

At this point we're not very far from the final result. All we need to do is to redraw the chart data when new data are available. It could came from an API or any other data sources but in this article we will simulate the new data flow with random data.

{% highlight javascript %} constructor() { const me = this;

setInterval(function () {
  if (me._chart) {
    me._chart['series'][0].addPoint([(new Date()).getTime(), me.randomValue()], true, true);
  }
}, 2000);

} {% endhighlight %}

The above code takes place in the component's constructor. To avoid any problems with this inheritance in functions we prefered to create a me variable.

Every two seconds a random Number value will be generated by randomValue(). Every Highcharts instances has an addPoint() method designed to easily add new values to the chart data without manipulating directly the data array itself.

What is cool with addPoint() method is that it adds the data passed in argument to the chart data and automatically redraw() it. You can also manually slice() and push() data in the data array (in the chart opts object) but you'll be forced to redraw the chart manually e.g. me._chart['redraw']() but that is not the best way to proceed.

The addPoint() method takes four arguments, the first is the point options that could be a Number, an Array or an Object. It represents the new value you want to add to the chart data. A chart point can be a number, or an object composed of a xAxis value and an yAxis value. In our chart example xAxis represents the categories and yAxis the points. That's why we set as first argument an Array with the xAxis value (the current date) and the yAxis value (the randomized number).

The second and third arguments are Boolean values designed to tell the function if it has to redraw() the chart and slice() the data array. So this is exactly what we're looking for!

Then the fourth and last argument is an animation which defaults to true. When true, the graph will be animated with default animation options. The animation can also be a configuration object with properties duration and easing. So there is no need to set it.

Final working solution

<iframe style="width: 100%; height: 520px" src="//embed.plnkr.co/lGzThF?show=preview" frameborder="0" allowfullscren="allowfullscren"></iframe>

Feel free to ask any questions you may have!