Creating Charting Directives Using AngularJS and D3.js


D3 is a JavaScript library that can be used to create interactive charts with the HTML5 technology Scalable Vector Graphics (SVG). Working directly with SVG to create charts can be painful, as one needs to remember the shapes supported by SVG and make several calls to the API to make the chart dynamic. D3 abstracts most of the pain, and provides a simple interface to build SVG-based charts. Jay Raj published two nice SitePoint articles on working with D3, check them out if you are not already familiar with D3.

Most of you may not need a formal introduction to AngularJS. AngularJS is a client side JavaScript framework for building rich web applications. One of the top selling points of AngularJS is the support for directives. Directives provide an excellent way to define our own HTML properties and elements. It also helps in keeping the markup and code separated from each other.

AngularJS is very strong in data binding as well. This feature saves a lot of time and effort required to update the UI according to data in the model. In the modern web world, customers ask developers to build websites that respond in real-time. This means the customers want to always see the latest data on the screen. Data UI has to be updated as soon as someone modifies a piece of data in the back-end. Performing such real-time updates would be very difficult and inefficient if we don’t have support of data binding.

In this article, we will see how to build real-time AngularJS directives that wrap D3 charts.

Setting Up

First, we need to set up the environment. We need AngularJS and D3 included in the HTML page. As we will build just a chart directive, we need to create an AngularJS controller and a directive. In the controller, we need a collection holding data to be plotted in the chart. The following snippet shows the initial controller and directive. We will add more code to these components later.

var app = angular.module("chartApp", []);

app.controller("SalesController", ["$scope", function($scope) {
  $scope.salesData = [
    {hour: 1,sales: 54},
    {hour: 2,sales: 66},
    {hour: 3,sales: 77},
    {hour: 4,sales: 70},
    {hour: 5,sales: 60},
    {hour: 6,sales: 63},
    {hour: 7,sales: 55},
    {hour: 8,sales: 47},
    {hour: 9,sales: 55},
    {hour: 10,sales: 30}

app.directive("linearChart", function($window) {
    restrict: "EA",
    template: "<svg width='850' height='200'></svg>",
    link: function(scope, elem, attrs){

We will fill the link function in the above directive to use the data stored in the controller and plot a line chart using D3. The template of the directive contains an svg element. We will apply D3’s API on this element to get the chart plotted. The following snippet shows an example usage of the directive:

<div linear-chart chart-data="salesData"></div>

Now, let’s gather the basic data needed for plotting the chart. It includes the data to be plotted, JavaScript object of the SVG element, and other static data.

var salesDataToPlot=scope[attrs.chartData];
var padding = 20;
var pathClass = "path";
var xScale, yScale, xAxisGen, yAxisGen, lineFun;
var d3 = $window.d3;
var rawSvg = elem.find("svg")[0];
var svg =;

Once the library for d3 is loaded, the d3 object is available as a global variable. But, if we use it directly inside a code block, it is hard to test that block of code. To make the directive testable, I am using the object through $window.

Drawing a Simple Line Chart

Let’s set up the parameters needed to draw the chart. The chart needs an x-axis, a y-axis, and the domain of data to be represented by these axes. In this example, the x-axis denotes time in hours. We can take the first and last values in the array. On the y-axis, the possible values are from zero to the maximum value of sales. The maximum sales value can be found using d3.max(). The range of the axes vary according to the height and width of the svg element.

Using the above values, we need to ask d3 to draw the axes with the desired orientation and the number of ticks. Finally, we need to use d3.svg.line() to define a function that draws the line according to the scales we defined above. All of the above components have to be appended to the svg element in the directive template. We can apply the styles and transforms to the chart while appending the items. The following code sets up the parameters and appends to the SVG:

function setChartParameters(){
  xScale = d3.scale.linear()
             .domain([salesDataToPlot[0].hour, salesDataToPlot[salesDataToPlot.length - 1].hour])
             .range([padding + 5, rawSvg.clientWidth - padding]);

              yScale = d3.scale.linear()
                .domain([0, d3.max(salesDataToPlot, function (d) {
                  return d.sales;
             .range([rawSvg.clientHeight - padding, 0]);

  xAxisGen = d3.svg.axis()
               .ticks(salesDataToPlot.length - 1);

  yAxisGen = d3.svg.axis()

  lineFun = d3.svg.line()
              .x(function (d) {
                return xScale(d.hour);
              .y(function (d) {
                return yScale(d.sales);
function drawLineChart() {


     .attr("class", "x axis")
     .attr("transform", "translate(0,180)")

      .attr("class", "y axis")
      .attr("transform", "translate(20,0)")

        d: lineFun(salesDataToPlot),
        "stroke": "blue",
        "stroke-width": 2,
        "fill": "none",
        "class": pathClass


Here is the demo showing the above chart.

Updating the Chart in Real Time

As stated earlier, with the capability of the web today, our users want to see the data charts updating immediately as the underlying data changes. The changed information can be pushed to the client using technologies like WebSockets. The chart directive that we just created should be able to respond to such changes and update the chart.

To push data through WebSockets, we need a component on server built using Socket.IO with Node.js, SignalR with .NET, or a similar technology on other platforms. For the demo, I used the $interval service of AngularJS to push ten random values of sales into the sales array with a delay of one second:

$interval(function() {
  var hour = $scope.salesData.length + 1;
  var sales = Math.round(Math.random() * 100);

  $scope.salesData.push({hour: hour, sales: sales});
}, 1000, 10);

To update the chart as soon as the new data is pushed, we need to redraw the chart with updated data. A collection watcher has to be used in the directive to watch the changes on the collection data. The watcher is invoked when any change is made to the collection. The chart is redrawn in the watcher.

scope.$watchCollection(exp, function(newVal, oldVal) {
  salesDataToPlot = newVal;

function redrawLineChart() {


  svg.selectAll("." + pathClass)
       d: lineFun(salesDataToPlot)

The complete demo can be found here.


AngularJS and D3 are very useful libraries for building rich business apps on the web. We discussed how to use them together to create a simple chart. You can extend this knowledge for creating the charts for your applications.