Proper use of D3.js with Angular directives

D3.js is not only an eye-candy way to create data graphics, as a library proposes a very powerful concept: manipulate documents (read DOM) based on data. So instead of feeding a library with input data to draw a graph, D3.js creates a binding between your data source and the DOM, with all the dynamic benefits that this implies.

Although it’s something completely different, AngularJS also implements the same underlying philosophy with it’s all famous two-way data binding: have your models as plain JavaScript objects, and represent it directly modifying the DOM. continuously changing the view whenever the DOM changes, having only “one source of truth”.

AngularJS does most of this magic via setting watcher on your JavaScript plain objects and re-rendering the DOM via $digest cycle. Hence you have to be kinda careful whenever modifying the DOM directly, and as a guideline, you shouldn’t do DOM modifications in your controllers directly, and rather choose directives to do this job. From the official documentation:

Stop trying to use jQuery to modify the DOM in controllers. Really. That includes adding elements, removing elements, retrieving their contents, showing and hiding them. Use built-in directives, or write your own where necessary, to do your DOM manipulation.

So, I’ve seen many tutorial and examples using AngularJS as framework but also D3.js for data visualization that actually miss this point and do terrible things.

What would be “the Angular way” of integrating D3? I propose the following:

  1. All your D3 logic and presentation must be contained within a directive
  2. Use HTML-declarative syntax to feed of data to your directive instances
  3. By doing that, you can store the data in your controller, passing it to your D3 directive via two-way data binding of parameters

Let’s illustrate that with an example.

All your D3 logic and presentation must be contained within a directive

This part assumes you’re familiar with AngularJS directives, if not, please watch this awesome video. First of all, think how you want to abstract the behavior of your data visualization: you might want a directive to draw D3 SVG  shapes, or charts (pie, chart, maps, anything you want). In this example, let’s create a D3 graphic that plots an array of data into a bars chart.

Use HTML-declarative syntax to feed of data to your directive instances

This is why we wanted a directive in the first place. It’s one of the neatest things of AngularJS: extending HTML. So now, whenever we want to declare a new bar chart in your HTML mark-up, we’d be including something like this:

Notice how the HTML itself remains completely agnostic of how the chart is being draw. This time we’re using D3, but we may change the implementation in the future, or do even greater things using the latest D3 version. Whatever happens, out HTML remains the same. That’s modularity yo!

Store the data in your controller, passing it to your D3 directive via two-way data binding of parameters

We don’t really want to have a hardcoded chart-data=”40,100,80,15,25,60,10″ in your HTML, since that information is meant to be in our models: our source of truth. So the right place for it is in our controller’s $scope variable:

And now we can feed this myData array to our directive view

Our directive will need some work, binding its scope to this new data source given by its parameter chart-data

See everything (HTML, JS and CSS) in my Github Gist or watch it in action below:

See it Live in CodePen!

So now we have it, a correctly place, semantically beautiful and modular data visualization with AngularJS and D3! what are your thoughts?

 

  • Kabby

    Thank you for so clearly explaining this! Other explanations I have found online only confused me…your post was easy to follow and worked on the first try!

  • brentchow

    Thanks! This was great!

  • levelsoft

    Great starter tutorial – thanks a lot

  • Dhruv Kumar Arora

    I opened it on CodePen and pasted it in my editor and ran it and it is giving an error “Uncaught Error: No module: myApp”. Please help!

  • http://perrohunter.com perrohunter

    Nice code!

  • Leo Ribeiro

    Niicee!!! Great code!

  • Moses Xaba

    You have to have 3 files(Html,js and css) and when you paste the html script just complete it by having your full html code(with html, head, body tags) then reference the js and css files into ur html file. And dont forget to remove the script tags to ref to Angular and d3.js into the header.