Creating Charting Directives Using AngularJS and D3.js

Share this article

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) {
  return{
    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 = d3.select(rawSvg);

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()
               .scale(xScale)
               .orient("bottom")
               .ticks(salesDataToPlot.length - 1);

  yAxisGen = d3.svg.axis()
               .scale(yScale)
               .orient("left")
               .ticks(5);

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

  setChartParameters();

  svg.append("svg:g")
     .attr("class", "x axis")
     .attr("transform", "translate(0,180)")
     .call(xAxisGen);

   svg.append("svg:g")
      .attr("class", "y axis")
      .attr("transform", "translate(20,0)")
      .call(yAxisGen);

   svg.append("svg:path")
      .attr({
        d: lineFun(salesDataToPlot),
        "stroke": "blue",
        "stroke-width": 2,
        "fill": "none",
        "class": pathClass
   });
}

drawLineChart();

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;
  redrawLineChart();
});

function redrawLineChart() {

  setChartParameters();
  svg.selectAll("g.y.axis").call(yAxisGen);
  svg.selectAll("g.x.axis").call(xAxisGen);

  svg.selectAll("." + pathClass)
     .attr({
       d: lineFun(salesDataToPlot)
     });
}

The complete demo can be found here.

Conclusion

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.

Frequently Asked Questions (FAQs) about Creating Charting Directives Using AngularJS and D3.js

How can I integrate D3.js with AngularJS for data visualization?

Integrating D3.js with AngularJS for data visualization involves a few steps. First, you need to include the D3.js library in your AngularJS project. You can do this by downloading the library and including it in your HTML file or by using a CDN. Once you have included D3.js, you can start using it in your AngularJS directives. You can create a directive that uses D3.js to create a chart, for example. In the directive’s link function, you can use D3.js to select the directive’s element and create the chart. You can also use AngularJS’s data binding to update the chart when your data changes.

What are the benefits of using D3.js with AngularJS?

D3.js is a powerful library for creating data visualizations, and AngularJS is a popular framework for building web applications. By using them together, you can create dynamic, interactive data visualizations that are integrated with your web application. D3.js provides a lot of flexibility and control over your visualizations, while AngularJS provides a structured way to build your application and handle updates to your data.

Can I use D3.js with other versions of Angular?

Yes, you can use D3.js with other versions of Angular. The process is similar to using it with AngularJS. You would include the D3.js library in your project and then use it in your Angular components. You can use Angular’s data binding to update your visualizations when your data changes.

How can I handle updates to my data in D3.js visualizations?

D3.js provides a powerful data binding system that you can use to handle updates to your data. You can use the enter, update, and exit selections to specify how elements should be created, updated, or removed when your data changes. In combination with AngularJS’s data binding, you can create dynamic visualizations that update in response to changes in your application’s data.

What types of charts can I create with D3.js and AngularJS?

With D3.js and AngularJS, you can create a wide variety of charts, including bar charts, line charts, pie charts, scatter plots, and more. D3.js provides a lot of flexibility and control over your visualizations, so you can create custom charts that fit your specific needs.

Are there any resources for learning more about D3.js and AngularJS?

Yes, there are many resources available for learning more about D3.js and AngularJS. The official documentation for both libraries is a great place to start. There are also many tutorials, blog posts, and online courses that can help you learn more about creating data visualizations with D3.js and AngularJS.

Can I use D3.js with other JavaScript frameworks?

Yes, D3.js is a standalone library that can be used with any JavaScript framework or no framework at all. It is not tied to AngularJS or any other framework. You can use D3.js with React, Vue.js, Ember.js, or any other JavaScript framework that you prefer.

How can I handle user interactions in D3.js visualizations?

D3.js provides a variety of ways to handle user interactions. You can use D3.js’s event handlers to respond to user events like clicks, mouse movements, and keyboard input. You can also use D3.js’s transitions to animate changes to your visualizations in response to user interactions.

What are the performance considerations when using D3.js with AngularJS?

When using D3.js with AngularJS, it’s important to be mindful of performance. Both libraries provide ways to optimize performance, such as using D3.js’s data joins to minimize DOM manipulation and using AngularJS’s digest cycle efficiently. It’s also important to be mindful of the size of your data and the complexity of your visualizations, as these can impact performance.

Can I use D3.js with AngularJS to create real-time data visualizations?

Yes, you can use D3.js with AngularJS to create real-time data visualizations. You can use AngularJS’s data binding to update your visualizations in response to changes in your data, and you can use D3.js’s transitions to animate these changes. This can be useful for visualizing real-time data, such as stock prices or sensor readings.

Rabi Kiran (a.k.a. Ravi Kiran) is a developer working on Microsoft Technologies at Hyderabad. These days, he is spending his time on JavaScript frameworks like Angular JS, latest updates to JavaScript in ES6 and ES7, Web Components, Node.js and also on several Microsoft technologies including ASP.NET 5, SignalR and C#. He is an active blogger, an author at SitePoint and at DotNetCurry. He is rewarded with Microsoft MVP (ASP.NET/IIS) and DZone MVB awards for his contribution to the community.

Angular Tutorialschart designD3.js
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week