Using Angular and Chart.js to build real-time charts
Tags: angular, javascript, typescript, webdev
Published: 2020-08-10 12:56:05

---

firstPublishedAt: 1597055526219
latestPublishedAt: 1597055526219
slug: using-angular-and-chart-js-to-build-real-time-charts
title: Using Angular and Chart.js to build real-time charts

---
[Angular](https://angular.io/) and [Chart.js](https://www.chartjs.org/) is popular combination when creating any data visualization application. The first one can handle a very large throughput of data and the later is capable of rendering the plots in real-time, thanks to Canvas API. In this post I will guide you through the process of creating a real-time chart using Angular and Chart.js

# Prerequisites

Before starting to write any code, make sure you have the following:

- [Node.js](https://nodejs.org/en/) — I use version 13.2.0

- [Angular CLI](https://cli.angular.io/) — I use version 8.3.20

- 10 minutes of free time

# Creating new Angular project

The first step required is to create a new Angular project. As mentioned in prerequisites, I am using Angular CLI to do so and I highly advise you to do the same. Open a terminal window, navigate to desired directory and execute the command:

```
ng new angular-charts --routing=true --styling=scss
```

This command creates a new Angular project called _angular-charts_ in directory of the same name. Additionally, I have added two optional flags — `routing` adds the router module to the app and `styling` sets the extensions of style sheets used.

With the project created, open it up in your IDE of choice — I will be using Visual Studio Code for this.

# Adding a service layer\*

The next step of this tutorial is to add a service layer. I have marked this step with asterisk, because it is **optional**. If you already have one, or you don’t need one, then feel free to skip this section.

Let’s start this section with generating a service that will give access to real-time data source using `Observable`. To generate a service, use the following command:

```
ng generate service sse
```

After executing the command, and `SseService` gets created and that's where the service layer code will be placed. For this tutorial I am using SSE or Server Sent Events data source, tutorial on which you can find [here](https://bartoszgajda.com/2019/12/22/angular-and-server-sent-events-sse/). If you need more explanation, don't hesitate to read that tutorial. To avoid repetition in this post, I will just paste in the following:

```
import { Injectable, NgZone } from "@angular/core";
import { Observable } from "rxjs";
@Injectable({
  providedIn: "root"
})
export class SseService {
  constructor(private _zone: NgZone) {}
  getServerSentEvent(url: string): Observable<any> {
    return Observable.create(observer => {
      const eventSource = this.getEventSource(url);
      eventSource.onmessage = event => {
        this._zone.run(() => {
          observer.next(event);
        });
      };
      eventSource.onerror = error => {
        this._zone.run(() => {
          observer.error(error);
        });
      };
    });
  }
  private getEventSource(url: string): EventSource {
    return new EventSource(url);
  }
}
```

# Hooking up the Chart.js

The next step is to hook in Chart.js library into our Angular project. There are couple ways to do so, but I will use a dedicated package, called [**Ng2-Charts**](https://valor-software.com/ng2-charts/). This package exposes a much nicer API while retaining all the required functionality. In my case, I add the following dependencies to my `package.json` file:

```
"chart.js": "^2.9.3",
"ng2-charts": "^2.3.0",
```

After modifying the `package.json` file, don't forget to run either `npm install` or `yarn` depending on your package manager.

# Adding HTML template

Going further, we have to add an HTML template that will render the chart. In the case of this tutorial, you can place it anywhere you fancy — the code is single HTML tag with custom properties which we will explore in next step. I place it in a component HTML template called `count-events.component.html`. The HTML template should include the following:

```
<canvas
    width="600"
    height="400"
    [datasets]="countEventsData"
    [chartType]="countEventsChartType"
    [labels]="countEventsLabels"
    [colors]="countEventsColors"
    [options]="countEventsOptions"
></canvas>
```

I have placed my chart is count-events folder, therefore all variables are prepended with those. In the `canvas` tag we specify height, width and variable configuration, which will be placed in corresponding `.ts` file.

# Configuring Chart.js

As mentioned in chapter above, we will add some custom configuration to the Chart.js plots. This configuration will be placed in your components’ TypeScript file, in my case it is called `count-events.component.ts`.

The first thing that has to be set is the `datasets` property. That is a container that will hold the data displayed on the plot itself. The code for this should look like below:

```
countEventsData: ChartDataSets[] = [
  { data: [], label: "Number of Events", fill: false }
];
```

This variable is an array, meaning you can have many data sets displayed on a single plot. Inside of each element there are three core parts:

- `data` - an array that holds the single values to be displayed on the chart

- `label` - label of the data set

- `fill` - configuration option setting the appearance of the data set on chart

Next configuration is the `chartType` property. That is a single string, flagging the type of chart that should be used. There is a wide variety of options available, including line, bar, graph or pie, but for this tutorial we are going to stick with the simplest one - line:

```
countEventsChartType = "line";
```

Going further, `labels` property has to be set. This element sets what labels the **X** axis receives. In our case however, we don't want to set them as a constant. We want to be able to update the labels in real-time, in cnjuction with the incoming data. This property therefore is set as empty array:

```
countEventsLabels: Label[] = [];
```

Next property is `colors`. The name itself is probably self explanatory, so I will jump straight to code:

```
countEventsColors: Color[] = [
    {
      borderColor: "#039BE5",
      pointBackgroundColor: "#039BE5"
    }
];
```

Last bit of configuration is called `options`. That is the central configuration point, for all major flags that can be set. The amount of available options is very broad, so please refer to [Chart.js docs](https://www.chartjs.org/docs/latest/general/options.html) for complete documentation. In our case, we are only interested in removing the animations - that will optimize the chart and make it run faster. To do this, paste the following into your code:

```
countEventsOptions: ChartOptions = {
    animation: {
      duration: 0
    }
 };
```

# Connecting Service and Chart.js

Last chapter of this tutorial will show you how to glue the service and the Chart.js together. To make this happen, we will implement couple functions in the `count-events.component.ts` file.

We start off with subscribing to the data source, which is an `SseService` in our case. That is done in the `ngOnInit` hook, so that we connect to data source whenever our component is loaded in the application. In here, we create a `Subscription` to the endpoint and call `pushEventToChartData` function.

```
private countEventsSubscription$: Subscription;
ngOnInit() {
    this.countEventsSubscription$ = this.sseService
      .getServerSentEvent("http://localhost:8082/count-events")
      .subscribe(event => {
        let data = JSON.parse(event.data);
        this.pushEventToChartData(data);
      });
  }
```

The aforementioned function have a simple purpose — it checks whether the `datasets` have reached an arbitrary limit (20 in this case) and if so, removes the last element before pushing the new one into this collection. On thing has the kept in mind - if adding or removing elements, it has to be done for both `datasets` collections and labels `collections`. Both of them have to by kept synced all the time.

```
private pushEventToChartData(event: CountEvents): void {
    if (this.isChartDataFull(this.countEventsData, 20)) {
      this.removeLastElementFromChartDataAndLabel();
    }
    this.countEventsData[0].data.push(event.count);
    this.countEventsLabels.push(
      this.getLabel(event)
    );
  }
```

The last pieces of code include the helper functions calls to which can be found in snippet above. First functions could be used to implement some prettier looking labels. Second one removes the last element from both `datasets` and `labels` collections. The third checks whether the a collections has reached its limit, which I have set to be 20 in my case. The snippets for those are as follows:

```
private getLabel(event: CountEvents): string {
    return `${event.window}`;
  }

  private removeLastElementFromChartDataAndLabel(): void {
    this.countEventsData[0].data = this.countEventsData[0].data.slice(1);
    this.countEventsLabels = this.countEventsLabels.slice(1);
  }

  private isChartDataFull(chartData: ChartDataSets[], limit: number): boolean {
    return chartData[0].data.length >= limit;
  }
```

Wrapping this all up, the complete code for `count-events.component.ts` file looks like this:

```
export class CountEventsComponent implements OnInit, OnDestroy {
  private countEventsSubscription$: Subscription;
  private eventsOnChartLimit = 20;
  countEventsChartType = "line";
  countEventsData: ChartDataSets[] = [
    { data: [], label: "Number of Events", fill: false }
  ];
  countEventsLabels: Label[] = [];
  countEventsColors: Color[] = [
    {
      borderColor: "#039BE5",
      pointBackgroundColor: "#039BE5"
    }
  ];
  countEventsOptions: ChartOptions = {
    animation: {
      duration: 0
    }
  };

  constructor(private sseService: SseService) {}

  ngOnInit() {
    this.countEventsSubscription$ = this.sseService
      .getServerSentEvent("http://localhost:8082/count-events")
      .subscribe(event => {
        let data = JSON.parse(event.data);
        this.pushEventToChartData(data);
      });
  }

  private pushEventToChartData(event: CountEvents): void {
    if (this.isChartDataFull(this.countEventsData, 20)) {
      this.removeLastElementFromChartDataAndLabel();
    }
    this.countEventsData[0].data.push(event.count);
    this.countEventsLabels.push(
      this.getLabel(event)
    );
  }

  private getLabel(event: CountEvents): string {
    return `${event.window}`;
  }

  private removeLastElementFromChartDataAndLabel(): void {
    this.countEventsData[0].data = this.countEventsData[0].data.slice(1);
    this.countEventsLabels = this.countEventsLabels.slice(1);
  }

  private isChartDataFull(chartData: ChartDataSets[], limit: number): boolean {
    return chartData[0].data.length >= limit;
  }

  ngOnDestroy() {
    this.countEventsSubscription$.unsubscribe();
  }
}
```

And that this tutorial finished. Using Angular and Chart.js is not a rocket science and the benefits of having a real-time charts can be huge.

# Summary

I hope you have found this post useful. If so, don’t hesitate to like or share this post. Additionally you can follow me on my social media if you fancy so :)

{% twitter 1275766441925640192 %}