Playing Nice: Angular & D3.js
In this post I would like to talk about how you can embed a D3.js powered Visualisation Framework in an application running on Angular.js.
For more background reading on the basic concepts behind such a viz framework, feel free to check out my previous post: Putting the Viz in Dataviz
When combining external JS frameworks and libraries, especially ones that are used for central parts of an application, it pays off to take a step back and think about possible strategies:
A major concern should be to figure out what each framework’s strengths and weaknesses are and how you can best exploit those. At the same time, coming up with a plan for managing overlapping functionality and potential incompatibilities is key.
For example if you have two frameworks that give you the ability to manipulate the DOM easily, try and figure out a preferred set of methods and framework to use for that use case. By agreeing on standard patterns like that you will create a clear separation of responsibility. This might save you headaches in the long run when it comes to better code structure and readability which could also help future on-boarding. As another result the loose coupling and well defined usage patterns will make it easier to swap out frameworks and libraries for other ones in the future.
But much more interesting is getting different frameworks’ strong suits to benefit from each other. So here are some hands-on examples of how we can get Angular and D3.js to play well together:
Dependency injection is a well-defined design pattern and generally nothing new in the world of software development. However, a great feature of Angular.js is making some of DI’s core concepts accessible to use through the framework.
For a more detailed description of Angular’s implementation have a read in their Developer Guide. The TL;DR on it: For one we can use DI to enable us to structure our code more modular – but mainly it lets us define what other components a particular part of our application need in order to operate. So in other words what it depends on. DI then makes sure that those other components are instantiated and available to be used before the current component is executed.
Since Angular is making DI so beautifully simple to use in a front-end application we should absolutely exploit it! By making D3 itself a dependency we can inject it into other parts of our application and with that we achieve a couple of benefits:
- The D3 library is only loaded when needed reducing footprint and freeing up resources.
- The D3 library is actually loaded when needed. This means we don’t have to instatiate or load it ourselves and further it is guaranteed to be available when we need it so we don’t have to repeatedly check for its existence to avoid exceptions.
Reusable Components using Directives
There are a number of articles around talking about building “reusable charts” using D3. What that means is basically to make our D3 viz code less use-case specific and more reusable. And we can use Angular Directives to help us along the way!
Directives are basically custom components we can inject into our DOM by placing a custom element or attribute. This is great for elements we want to reuse around the application without repeating code for each instance.
For example, we can set up a directive that lets us render a simple sparkline. So whenever we want to render one, all we need to do is use the sparkline-directive and pass use-case specific configuration to it. That could be as simple as including <div viz-sparkline config=”configObject”></div> in a template.
The Angular directive would then take over and let us modify the element and contents using a number of hooks and functions for each instance.
Data Joins & Binding
D3’s Data Joins and Angular’s Data Binding are almost begging to be used together! Data joining in D3 gives us easy to use methods to associate data with DOM elements whilst staying reactive to changes in that data and updating the DOM accordingly. Angular’s data binding gives us a great way to expose and manipulate data in our model from both the view and controller layer.
By setting up our sparkline directive in a way it can react to data changes all that’s left is to pipe through the data from Angular when such changes happen!
Ok, enough talk let’s see some examples:
In this post we covered an example showing a couple of core concepts in both D3.js and Angular.js you can utilise to create an interactive data visualisation application. To recap:
- We created a very basic Angular.js based application with the main module called “myApp”.
- We setup an Angular Service Factory called “d3Service” and use it to inject and load the D3 library code into our application from an external resource (in this case a CDN). By wrapping this process in a promise (using Angular’s $q) we can make components that use D3 wait for it and resolve only once D3 is actually loaded and ready to be used.
- We then use a basic Controller to setup and bind some options and data to use through its $scope. In a more complex application data is more likely to come from external sources such as APIs or additional Services.
- By using some built-in Angular Directives (namely ng-model, ng-change, ng-options and ng-repeat) in our template we enable the user to alter the data and options held in our model (via $scope in this case) – which in turn enables the magical 2 way data bindingyou always hear about when looking at Angular.
- We then define our custom directive called “vizSparkline” to create a reusable component allowing us to render a sparkline visualisation by simply placing <div viz-sparkline></div> in our view. The scope option in our directive definition allows us to pass in configuration from our controller’s $scope like so: <div viz-sparkline config=”sparklineConfig”></div> where sparklineConfig would be an object on $scope.sparklineConfig in our controller.
Inside the link function we define what actually happens inside our <div viz-sparkline> by setting up a SVG element and ultimately paint the sparkline reflecting the passed in data and options.
Thanks for reading and have fun with it!