c3js directives for AngularJS

Posted on 2015-01-01 by

Some time a go I started experimenting with c3js, a nice library around the very popular D3 graphics library. It is all about creating nice looking charts with lots of options. When integrating this in an AngularJS application it is logical to create some reusable directives. The next step is to make these directives easily available to integrate in other projects. Nowadays the easiest way to do this in front-end projects is to use Bower. During development GruntJS helps with some tasks you just need to do.

In this blog post I am going to introduce you to the library, show you how I used it in my elasticsearch plugin and show some of the GruntJS functionality that help me during development.

The c3-angular project

The project is hosted on Github under the MIT license. Use it like you want to, comments are welcome and if you have requests or bugs please use the issue system in github.

https://github.com/jettro/c3-angular-sample

The directives are javascript only. The library is just a wrapper, I do not include any of the required libraries in the project. So this is something you have to do yourself. You have to include c3js, AngularJS, D3js and do not forget the css from c3js. A very basic wrapper for an AngularJS application using the directives looks like this.

<!doctype html>
<html ng-app="graphApp">
<head>
  <meta charset="utf-8">
  <link href="assets/css/c3.min.css" rel="stylesheet" type="text/css">
</head>
<body ng-controller="GraphCtrl" >
  <c3chart bindto-id="chart" chart-data="datapoints" chart-columns="datacolumns" chart-x="datax">
    <chart-axis>
      <chart-axis-x axis-id="x" axis-type="timeseries">
        <chart-axis-x-tick tick-format="%Y-%m-%d"/>
      </chart-axis-x>
    </chart-axis>
  </c3chart>

  <!-- Load the javascript libraries -->
  <script src="assets/js/d3.min.js" charset="utf-8"></script>
  <script src="assets/js/c3.min.js"></script>
  <script src="assets/js/angular.min.js"></script>
  <script src="assets/js/c3-angular.min.js"></script>
  <script src="js/graph-app-directive-2.js"></script>
</body>
</html>

This is not the very basic application where you also provide the data in the directive. This is possible, but usually you obtain the data using an AngularJS controller. In the first element of the directive we configure the scope objects that are attached to $scope in the controller. These are: chart-data, chart-columns and chart-x. The other elements in the directive deal with the configuration of the chart. Notice that we load all the required javascript libraries at the bottom and the css in the head. The next code block shows the javascript code for the controller and the service.

var graphApp = angular.module('graphApp', ['gridshore.c3js.chart','graphApp.services']);

graphApp.controller('GraphCtrl', function ($scope, $interval,dataService) {
	$scope.datapoints=[];
	$scope.datacolumns=[{"id":"top-1","type":"line","name":"Top one"},
	                    {"id":"top-2","type":"spline","name":"Top two"}];
	$scope.datax={"id":"x"};

	$interval(function(){
		dataService.loadData(function(data){
			$scope.datapoints.push(data);
		});		
	},1000,10);
});

var services = angular.module('graphApp.services', []);
services.factory('dataService', function() {
	function DataService() {
		var maxNumber = 200;

		// API methods
		this.loadData = function(callback) {
			callback({"x":new Date(),"top-1":randomNumber(),"top-2":randomNumber()});
		};

		function randomNumber() {
			return Math.floor((Math.random() * maxNumber) + 1);
		}
	}
	return new DataService();
});

Check the project if you need more information about the different options that you have. Below you’ll see an example of the chart

example-chart-c3-angular

You can checkout this sample and some others in the examples folder of the mentioned Github project.

Using Grunt and Bower

This project is made available using bower. You can checkout the available versions with the following command.

# bower info c3-angular

Than you can use it in your bower project using the following command. Beware that you have to include the AngularJS, C3js and D3 libraries yourself in your project.

# bower install c3-angular --save

During development I have used Gruntjs do to some tasks for me. In the past I use python to start a very basic webserver when I needed one. With grunt this becomes a very basic task as well. Just add the deserver plugin to your project. In my case I can now run the grunt command to start a webserver with the examples folder as the root on the default 8888 port. Below first the command to run and than the configuration of Grunt

grunt devserver
devserver: {
  options: {
    base: 'examples'
  },
  server: {}
}

Besides compacting and minifying the code I also use Grunt to watch the sources and copy changes to the examples project. That way when changing a javascript file I immediately can watch the browser and test the change. The following code block shows you the copy plugin configuration of grunt.

copy: {
    examples: {
        files: [
            {expand: true, flatten: true, src: ['bower_components/angularjs/angular.min.js'], dest: 'examples/assets/js/', filter: 'isFile'},
            {expand: true, flatten: true, src: ['bower_components/angularjs/angular.min.js.map'], dest: 'examples/assets/js/', filter: 'isFile'},
            {expand: true, flatten: true, src: ['bower_components/d3/d3.min.js'], dest: 'examples/assets/js/', filter: 'isFile'},
            {expand: true, flatten: true, src: ['bower_components/c3/c3.min.js'], dest: 'examples/assets/js/', filter: 'isFile'},
            {expand: true, flatten: true, src: ['bower_components/c3/c3.min.css'], dest: 'examples/assets/css/', filter: 'isFile'},
            {expand: true, flatten: true, src: ['c3-angular.min.js'], dest: 'examples/assets/js/', filter: 'isFile'},
            {expand: true, flatten: true, src: ['c3-angular.min.js.map'], dest: 'examples/assets/js/', filter: 'isFile'}
        ]
    },
    bysource: {
        files: [
            {expand: true, flatten: true, src: ['c3-angular.js'], dest: 'examples/assets/js/', filter: 'isFile'}
        ]  
    }
}

Notice how I copy the libraries from the bower_components to the right positions for the samples. Using this technique I make it possible for you to immediately try out the samples without having to do bower stuff first.

The Elasticsearch-gui implementation

The next step is to use the directives in another project. In my elasticsearch-gui project I wanted to have charts. Sounds like a good opportunity to try out the directives. You can find the project on github.

https://github.com/jettro/elasticsearch-gui

In the plugin I give the user the option to choose between a pie chart, and a few different bar charts. This choice is based on the type of elasticsearch aggregation you choose. You have to chose an appropriate field. The sample index I use contains all my music of iTunes. In the pie chart you have an overview based on genre and the bar chart is a histogram with periods of 10 years when the songs were released.

c3-angular-pie-chart
c3-angular-histogram-chart

This application is a bigger AngularJS application. We make use of routing and therefore we can use partial html documents. I am not going to copy paste the complete document in here. This is kinda big. Also the data retrieval is to big to discuss in this blogpost. Check the source code and if you have question contact me. Check the following 2 sources if you are interested.

https://github.com/jettro/elasticsearch-gui/blob/master/partials/graph.html
https://github.com/jettro/elasticsearch-gui/blob/master/javascript/controllers/GraphCtrl.js

In the next two code blocks I do show you the directive configuration of both the pie chart and the histogram chart.

<c3chart bindto-id="chartterm" chart-data="results" chart-columns="columns">
    <chart-legend show-legend="true" legend-position="right"/>
    <chart-size chart-height="400" chart-width="600"/>
</c3chart>

This is a basic chart with not so much configuration, the next chart is more complicated

<c3chart bindto-id="charthisto" chart-data="results" chart-columns="columns" chart-x="xaxis">
    <chart-legend show-legend="false"/>
    <chart-size chart-height="400" chart-width="600"/>
    <chart-axis>
        <chart-axis-x axis-position="outer-left"
                      axis-label="Number by {{aggregate.interval}}"
                      axis-type="category">
            <chart-axis-x-tick tick-fit="true"
                               tick-rotate="50"/>
        </chart-axis-x>
        <chart-axis-y axis-id="y"
                      axis-position="outer-right"
                      axis-label="Document count"
                      padding-bottom="0"
                      range-min="0"/>
        </chart-axis>
</c3chart>

Summarising

You should now have an idea about what you can do with the c3-angular directives. You have seen how to use it in your project. I am planning on adding more features the coming months. If there is something specific that you would like to see, let me know.

references

About Jettro Coenradie

I am a Software Developer / Architect with a lot of hands on experience in Java, AngularJS, Elasticsearch and lots of others tools. I like to use these technologies to help customers with the business challenges. On top of that I like to gather and share knowledge related to data analytics. I have experience with importing and transforming data as well as presenting and visualising the data. Currently I am working with tools like elasticsearch, logstash and Kibana but also D3 and C3 for graphics and other presentations.


1 Comment

  • Himanshu

    Hi,

    In C3, there is a property “connectNull” to connect the lines in case of empty data. I want to use same thing in c3-angular but i don’t know whats the right syntax and where to put that.

    In js, the syntax is –
    line : {
    connectNull : true
    }
    How to use the same in c3-angular?
    Can you please guide me on this?

    Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *