Creating D3.js Charts using AngularJS Directives

Posted on by Nicholas Thomson

This post is part of an ongoing series which will attempt to demonstrate and explain the Crunchinator. Each post will describe a different part of the technology behind the Crunchinator, and hopefully each will be useful as a stand-alone tutorial.

One of the first things we knew about the Crunchinator is that we wanted to use D3.js. The flexibility and power of D3 lends itself to stunning visualizations and graphs. We wanted to leverage that power for the charts in the Crunchinator.

We also knew we were going to use AngularJS. Because the Crunchinator is so front-end heavy, Angular was the clear choice to make sure we kept the code organized and easy to read. The declarative nature of Angular means we have a solid separation of code and presentation; as a developer, that’s a huge win.

Knowing that we were going to use D3.js for charts and AngularJS for our framework, it seemed obvious that the best way to integrate the two was to create directives for each of the chart types we wanted to display. We created a total of five or six different directives using D3. Eventually we decided against a few of the graphs and we trimmed that down to the four you currently see.

What is a directive?

Simply put, a directive is an addition to HTML that defines custom behavior. If you’ve spent any time using angular you’ve probably already run into a few: ngRepeat, ngModel, and ngView are a few common built-in directives. But to leverage the real power of Angular eventually you’ll have to write custom directives; that’s where the fun begins.

Writing a directive

Angular gives us the ability to write our own directives. Essentially, with Angular, we have the power to extend HTML and define new, custom behaviors. Below you’ll find an example of a simple directive I’ve named crD3Bars. cr is a prefix used to differentiate our directives from possible future HTML elements and directives written by third parties.

In this case, I’d like to restrict our directive to being an element, so we add the ‘restrict’ property with a value of ‘E’ to the directive’s definition.

We’ll also need some way for the directive to get the data to graph, so we’re adding data-binding to the scope.

You can see I’m attaching this directive to the crunchinatorApp.directives module. For yours, you’ll want to attach it to your own app’s directive module (or wherever you put your directives).

Integrating D3

Now that we have the outline of very simple directive we need to integrate D3 to show our graph. To keep this post simple, I’m going to assume you’ve included D3 in the global namespace and its accessible through the d3 variable. You should note that this will work, but its generally an ill-advised idea. You should be injecting your dependencies instead of putting them in the global namespace, but that’s outside of the scope of this post. You can check out this ng-newsletter article which describes injecting D3 as a dependency in detail.

Using D3

After we have access to D3 library, we want to start using it. The first thing we’ll need to do is append an svg to the directive’s element, its going to need a width and height. We’ll be using the svg we’ve just created to append all of our shapes to construct our graph

We’ll also want to use D3 to create our graph’s x and y scales as well as their respective axes.

Once that’s in place, we can begin to show the dataset using d3. To do that, we’re going to set up a watcher on scope.data. A watcher function will be fired any time a change is made to the watched object (data in our case). We want to call a render function whenever our data changes. This way, anytime we change data in our controller or through some other means, our graph will update accordingly. Here’s our directive so far:

Rendering the graph

Now that we have the render function firing whenever the data is changed, we need to make the render function actually call the necessary D3 code and append shapes to the svg. Here’s what that looks like:

There’s a lot going on here, so I’m going to walk through this one section at a time.

Setting the x and y domain

Here we’re simply setting the domain of the x and y scales. We have to do this in the render function because the domains are based on data which changes whenever render is run.

And my axes!

In this section we’re drawing/redrawing our x and y axes. We call .remove() to remove any already-present axes as we don’t want to draw over them if they already exist. We’re also doing a bit of formatting to get everything looking how we want.

Drawing our bars

Here we’re appending the bars to our svg. We set the x position and width of each bar based on our x scale. We also give each bar the bar class.

Animating the bars

This section sets the height and y attribute of each bar. We’ve added a transition() and duration(1000) call so that when the bar’s data updates the height is animated smoothly between the two data points.

Putting It All Together

We’ve finished the render function, now let’s see how the code looks all together.

Once your directive has been attached to your module, you can reference it in your views like so:

Make sure the myData object is attached to your scope. That would look something like this:

And that’s it! Hopefully this helps you with the structure and organization of your d3-based angular directives. If you have any questions, go ahead and ask them in the comments; I’m happy to help you out.

 
comments powered by Disqus