JavaScript
Article

Building a Chart Component with Angular 2 and FusionCharts

By Rohit Boggarapu

This article was peer reviewed by Vildan Softic. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

As a web developer, if there is something that you can’t miss, it is Angular 2. It is a complete rewrite of the popular JavaScript framework from Google and is constantly in news for all the right reasons. It offers some major improvements over the previous versions and that’s why we’re choosing it today to build some beautiful charts.

For the charts, we will use the JavaScript chart library provided by FusionCharts. It offers a good collection of charts and is compatible with all major browsers. Although FusionCharts offers a dedicated plugin for Angular, it is not yet compatible with Angular 2. So I am not going to use it and instead code directly using JavaScript and Angular 2. (Note: it is recommended you use the plugin if you are using Angular 1 in your app).

The chart we are going to plot will depict an interesting statistic—the revenue of five top tech companies (Amazon, Apple, Facebook, Google and Microsoft) and will have an option to switch between revenue data for 2014 and 2015. We will first go through the step-by-step process of creating charts in Angular 2. After building a basic chart, we will cover some advanced topics such as adding annotations and updating chart data.

As ever, you can download the code for this tutorial from our GitHub repo, or you can jump to a demo of the finished chart at the end of the article.

Angular 2 vs Angular 1.x

Angular 2 has some significant changes over its previous major version (Angular 1.x), for example its support for languages such as TypeScript and Dart, and the way it computes updates to the DOM. If you’d like to learn more about how Angular 1 concepts and techniques map to Angular 2, you can check out the official quick reference. If you are interested in migrating your app from Angular 1.x to Angular 2, you can read the official migration guide.

Although Angular 2 supports TypeScript and Dart, we will use native JavaScript to write the Angular 2 application in this tutorial because of its familiarity. Using TypeScript or Dart would also introduce an unnecessary build step.

Setup

There are number of ways to get up and running with an Angular 2 project. The easiest is probably to head over to the official site and follow their 5 Min Quickstart tutorial.

One slight caveat to this approach however, is that it relies on you having Node and npm installed on your machine. We do have a guide for this, but if you’d prefer to follow this tutorial without installing these, you can use the following template:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Angular 2 FusionCharts Demo</title>

    <!-- 1. Load custom CSS & fonts-->
    <link rel="stylesheet" href="styles.css">
    <link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:300' rel='stylesheet'>

    <!-- 2. Load Angular 2 specific libraries -->
    <script src="https://code.angularjs.org/2.0.0-beta.17/angular2-polyfills.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.17/Rx.umd.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.17/angular2-all.umd.dev.js"></script>

    <!-- 3. Load FusionCharts library-->
    <script src="https://static.fusioncharts.com/code/latest/fusioncharts.js"></script>

    <!-- 4. Load component -->
    <script src='main.js'></script>
  </head>
  <body>

    <!-- 5. Display the application -->
    <angular-chart>Loading...</angular-chart>
  </body>
</html>

Creating the Chart Component

Components are the building blocks of any Angular 2 application. They are reusable pieces of code consisting of a view, and some logic. If you’re familiar with Angular 1, you can think of them as directives with a template and a controller.

Here’s the basis of our chart component:

(function(chartApp){
  chartApp.AppComponent = ng.core.Component({
    selector: 'angular-chart',
    template: '<div>Chart will render here</div>'
  }).Class({
    constructor: function(){}
  });

  document.addEventListener('DOMContentLoaded', function() {
    ng.platform.browser.bootstrap(chartApp.AppComponent);
  });
})(window.chartApp || (window.chartApp = {}));

Let’s take a second to see what’s going on.

We start with an IIFE (immediately invoked function expression) which we use to namespace our app. We pass it window.chartApp as an argument, which is initialized to an empty object if it isn’t defined. This is where our application is going to live—in a single property on the global object.

Inside the IIFE we create our component (AppComponent) by chaining the Component and Class methods from ng.core (a collection of Angular’s core components). We’re passing the Component method a configuration object containing the following propeties:

  • selector: a simple CSS selector which specifies a host HTML element. Angular will create and display an instance of the component whenever it encounters a HTML element matching this selector.

  • template: the template to be used when the component is rendered. Currently we’re passing a string containing a placeholder <div> element, but ideally we should move this out into its own template.

The Class method is where we add behavior and event bindings for the template.

Having defined our basic component, we initialize it using Angular’s browser bootstrap function.

You should be able to run the code in your browser at this point and see the message “Chart will render here”.

Creating the Chart

Let’s move on to creating the chart and displaying some data for 2014.

To do this we need to use the FusionCharts constructor function to which we pass an object containing all configuration parameters for the chart instance:

  • type: the type of chart we wish to create
  • renderAt: the DOM selector into which our chart will be rendered
  • width and height: the chart dimensions
  • id: the ID of the generated chart
  • dataFormat: the format of data passed to the dataSource option
  • dataSource: the configuration for the actual chart, as well as the data it should display
new FusionCharts({
  "type": "column2d",
  "renderAt": "chart-container",
  "width": "550",
  "height": "400",
  "id": "revenue-chart",
  "dataFormat": "json",
  "dataSource": {
    "chart": {
      "yAxisName": "Revenue (In USD Billion)",
      "yAxisMaxValue": "200",
      ...
    },
    "data": [{
      "label": "Amazon",
      "value": "88.99"
    }, {
      "label": "Apple",
      "value": "182.8"
    }
    ...
    ]
  }
});

Here’s the complete configuration file.

If you are unsure as to what any of the chart options actually do, or if you would like to find out how else the appearance of the chart can be configured, you can refer to the Chart Attributes page in the FusionCharts documentation.

The other thing we have to do is update our template to include the container our chart should render into. You can either do this by specifying a string to the Component’s template property (as we did previously), or by moving the template into its own file and referencing it using templateUrl.

chartApp.AppComponent = ng.core.Component({
  selector: 'angular-chart',
  templateUrl: 'chart.html'
}).Class({
  ...
});

Either way, this is what our template should look like.

<div class="container">
  <h1>Revenue of Top Tech Companies (2014)</h1>
  <div id ="chart-container"></div>
</div>

Here’s a demo of what we have so far:

You can view the code for this demo on Plunker.

If you click through to the demo on Plunker, in the file main.js you might notice that we have separated the FusionCharts configuration data into its own file, which we are then fetching using Angular’s HTTP class. This is for the sake of clarity (it makes the Angular-specific code easier to follow) and also because making a request for the data is what you’d typically do in a real life scenario.

However, this is not absolutely necessary, and you’d get the same result by doing everything directly in the chartApp constructor:

(function(chartApp) {
  chartApp.AppComponent = ng.core.Component({
    selector: 'angular-chart',
    template: '<div class="container"><h1>Revenue of Top Tech Companies (2014)</h1><div id ="chart-container"></div></div>'
  }).Class({
    constructor: function() {
      FusionCharts.ready(function(){
        var revenueChart = new FusionCharts({
          // config data here
        }).render();
      });
    }
  });

  document.addEventListener('DOMContentLoaded', function() {
    ng.platform.browser.bootstrap(chartApp.AppComponent);
  });
})(window.chartApp || (window.chartApp = {}));

The only other thing to mention is that the initialization code is wrapped within the FusionCharts.ready method. This safeguards your chart instantiation code from being called before the FusionCharts library is loaded.

With the basic chart ready, it is time to add more functionality, such as using company logos instead of names and updating the chart with new data for the year 2015.

Adding Annotations

For adding company logos to the x-axis, we will use one of FusionCharts’ powerful features—annotations. Annotations on a FusionCharts object allow you to draw custom shapes or images at designated positions on the chart.

Suppose you want to add your company logo at the center of chart. You can do it using annotations and macros. Macros will give you the coordinates of the center of the chart and annotation will let you add an image at that location.

Things get interesting when you use dynamic annotations to, for example, get information on positions which are dependent on the chart’s data. Imagine you want to draw something exactly where the column ends. You can use the dynamic annotation macro $dataset.0.set.1.endX and $dataset.0.set.1.endY to determine the x and y coordinates of the column end point, then draw something over there. You can learn more about annotations and how to use them on this FusionCharts documentation page.

For our chart, we will use dynamic annotation macros to get each column’s starting and ending coordinates, which is where we will then draw the respective company logos. We will also disable default x-axis labels using the chart attribute "showLabels": "0".

To achieve the above goals, add the following code to the chart’s configuration:

{
  "type": "column2d",
  ...
  "dataSource": {
    "chart": {
      "showLabels": "0",
      ...
    },
    "data": [{ ... }],
    "annotations": {
      "groups": [{
        "id": "logo-images",
        "xScale": "30",
        "yScale": "30",
        "showBelow": "0",
        "items": [{
          "type": "image",
          "url": "https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2016/06/1465735364amazon.jpg",
          "x": "$dataset.0.set.0.startx + 25",
          "y": "$dataset.0.set.0.endY + 10"
        }, {
          "type": "image",
          "url": "https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2016/06/1465735362apple.jpg",
          "x": "$dataset.0.set.1.startx + 85",
          "y": "$dataset.0.set.1.endY + 10"
        }, {
          "type": "image",
          "url": "https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2016/06/1465735369facebook.jpg",
          "x": "$dataset.0.set.2.startx + 20",
          "y": "$dataset.0.set.2.endY + 10"
        }, {
          "type": "image",
          "url": "https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2016/06/1465735358google.jpg",
          "x": "$dataset.0.set.3.startx + 5",
          "y": "$dataset.0.set.3.endY + 5"
        }, {
          "type": "image",
          "url": "https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2016/06/1465735372microsoft.jpg",
          "x": "$dataset.0.set.4.startx + 30",
          "y": "$dataset.0.set.4.endY + 10"
        }]
      }]
    }
  }
}

In the above code:

  • type is setting the type of annotation.
  • url is setting the address of the image.
  • x and y are setting the starting x and y-coordinates of the images.

After adding the above code you should see company logos rendered on the x-axis. To learn more about using annotations and what else is possible, please refer to the documentation page (mentioned above).

Toggling Between Datasets

The final thing we want to implement is to allow the user to toggle between years, seeing a different dataset depending on the year selected (2014 or 2015).

Structuring the data.

We therefore need to consider how to structure our data in a way that we can define different datasets for the different years. As mentioned previously, FusionCharts is expecting the configuration options to contain a data property, which should be an array containing sets of label/value pairs.

{
  "type": "column2d",
  ...
  "dataSource": {
    "chart": {},
    "data": [
      {
        "label": "whatever",
        "value": "a value"
      }
    ]
  }
}

One way of handling multiple datasets would be to define a dataSet object at the top of our constructor function and attach it to the constructor using an alias.

var chartComp= this;
chartComp.dataSet = {
  "2014": [{
    "label": "Amazon",
    "value": "88.99"
  }, {
    "label": "Apple",
    "value": "182.8"
  }, {
    "label": "Facebook",
    "value": "12.47"
  }, {
    "label": "Google",
    "value": "65.67"
  }, {
    "label": "Microsoft",
    "value": "86.83"
  }],
  "2015": [{
    "label": "Amazon",
    "value": "107.01"
  }, {
    "label": "Apple",
    "value": "233.72"
  }, {
    "label": "Facebook",
    "value": "17.93"
  }, {
    "label": "Google",
    "value": "74.54"
  }, {
    "label": "Microsoft",
    "value": "93.58"
  }]
}

Then, in the configuration options we pass to the FusionCharts constructor, we can do:

"data": chartComp.dataSet['2014'],

Updating Chart Data on Toggle

We also want the chart to be updated with the data for 2015 once somebody clicks the 2015 button and toggle back to showing the data for 2014, when the 2014 button is clicked.

Let’s add the two buttons, which will be used to perform this action and give them some styling. Amend the component template as follows:

<div class="container">
  <h1>Revenue of Top Tech-companies</h1>

  <div class="actions">
    <button (click)="yearChange(2014)"
            [ngClass] = "{selected: selectedYear== 2014}">2014
    </button>
    <button (click)="yearChange(2015)"
            [ngClass] = "{selected: selectedYear== 2015}">2015
    </button>
  </div>
  <div id ="chart-container"></div>
</div>

Notice the new syntax for adding an event listener and adding the ngClass directive in Angular 2. They are almost the same as Angular 1, barring some braces and parentheses.

I’ve added an ngClass directive to highlight the current selected year by applying a selected CSS class to button element. This is based on the selectedYear property on the component which gets updated on the click of buttons.

We can set the current selected year to 2014 when the component renders by adding the following line to the top of the constructor:

chartComp.selectedYear = 2014;

The logic to handle the button clicks will be added to a new yearChange function.

.Class({
  constructor: function(){ ... },
  yearChange: function(){ // button logic here }
);

For this we’re going to use FusionChart’s setChartData method which requires both chart configuration options and the actual chart data. Instead of storing the chart attributes first and then referencing them, we will get the chart attributes from the chart that is already rendered, using the getChartData method and modify that data with year specific data.

yearChange: function(year) {
  var revenueChart = FusionCharts.items['revenue-chart'];
  var chartJson = revenueChart.getChartData('json');
  chartJson.data = this.dataSet[year];
  revenueChart.setChartData(chartJson);
  this.selectedYear = year;
}

After adding the HTML for buttons and the above click handler for those buttons, clicking on those buttons should load that year’s data for the chart.

Demo

And here’s the final demo.

You can view the code for this demo on Plunker. Or you can download the code from our GitHub repo.

If you click through to the Plunker, you’ll see that we have defined the dataSet property in the config.json file directly. This keeps things much tidier in our component.

Conclusion and Next Steps

We started by building a simple Angular chart, and then went on to add more functionality to it using annotations and other FusionCharts’ APIs. But this is just the tip of the iceberg and a lot more can be done using both Angular 2 and FusionCharts. Some things that you can explore on your own:

  • Include more charts: A column chart is not always the best way to represent a dataset. Depending on your use case you might want to use different charts like waterfall, radar, gauge etc. So try using the process explained in this tutorial to plot a different chart and see if you are able to do it successfully.

  • Include charts in your app: If you are into making hybrid mobile apps, then you must be aware that Ionic 2(latest version of Ionic) is based on Angular 2. And that’s a good news because you can use this tutorial as a base to create charts for your Ionic apps as well.

  • Explore more events: In this tutorial, I explained how to use setChartData method, but there are plenty more events and methods that you can use to enhance your app’s user experience. Check out the above linked pages to learn more about the various events and methods offered by FusionCharts.

If you face any difficulty while trying to make charts on your own, please refer to documentation of Angular or FusionCharts (depending on the issue), or just leave a comment below. I will be more than happy to help!

  • Brian

    Strange decision to write this tutorial for JavaScript. Angular examples, documentation, and best practice is for TypeScript by default. Every example I’ve seen released by the Angular team is TypeScript based.

  • ahuva

    thank you for the great tutorial. the link for the final demo points to the first one.
    the correct link i beleive is this: https://plnkr.co/edit/xs4Zx7?p=preview

  • Satish Pokala

    Hi thank you for the nice tutorial,

    I am using routing in angular 2 with latest rc5 so I have to use base href tag in my index.html
    If I use that my chart is not rendering properly, palette colors are missing .

    If I use base href tag I am unable to render fusion chart.

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in JavaScript, once a week, for free.