Data Visualization with D3.js and Angular JS: to join or not to join?

Data. It’s a buzzword right now, and hard to ignore in any technical profession. Bringing data into user experience gracefully is a challenge, and an area of interest for software engineers, graphic designers, scientists and in business generally. In an age where design and user experience on the web are of paramount importance to the success of a site, so too are the graphics and visualizations those sites can offer.

This blog post will focus on one such solution to the calling: D3.js and Angular JS. It highlights an elegant pairing of these two libraries and some clear use cases for them independently.

In order to fully engage with what the D3-Angular JS pairing can offer, it’s important to ask yourself what you’re trying to accomplish with the data you posses:

  • Is it all front-end? Where are you retrieving data from?
  • What is the desired effect of visualizing?
  • Do I need a dynamic visualization, or is my data static?

So after asking these fundamentals, where should you get started?

If you want a two-way interaction, something the user can manipulate, and you’re proficient in Angular JS, then find a visualization that suits your data from D3.js and create the pairing: something I’ll demonstrate later in this post.

If you’re new to either library and have time to play around, try to use each independently first. Angular JS and D3.js can both maneuver SVG graphics, HTML elements and static data independently and do so perfectly well. You will begin to see the benefits of each library for creating visualizations with data. In general, D3.js is the more straightforward to use if you’re new, as it is similar to jQuery and the syntax is direct. Angular JS, on the other hand, is a powerful framework used to build full web applications. As such, it has more complexity.

In a case where you have static data, don’t need user interaction, and want something to display the dataset, then creating a chart in D3.js alone makes sense, particularly if the styles provided by the library out of the box are something you or your client likes. As the D3.js docs suggest, it provides “efficient manipulation of documents based on data.” If you’re a beginner to D3.js, the links and resources at the bottom of this blog are a great starting point!

So let’s dive in!

Below is an example of D3.js and Angular JS using modifiable bar charts. I followed this GitHub library and corresponding tutorial to get started, you may also find it useful: https://github.com/EpiphanyMachine/d3AngularIntegration

To make D3.js available within the Angular JS code, first inject it into the Angular JS module:

// create the angular app
angular.module('d3App', [
    'd3App.controllers',
    'd3App.directives'
    ]);

// setup dependency injection
angular.module('d3', []);
angular.module('d3App.controllers', []);
angular.module('d3App.directives', ['d3']);

// inject D3.js
angular.module('d3')
    .factory('d3',[function(){
      var d3;

	d3=  // paste in a version of d3.min.js here

      return d3;
    }]);

Then you will need to include D3.js in a directive, which tells Angular JS to compile the HTML with special markers that will let Angular JS change elements in the DOM dynamically.

// Make your directive
angular.module('d3App.directives')
    .directive('d3Bars', ['d3', function(d3) {
        return {
            restrict: 'EA',
            scope: {
                data: "=",
                label: "@",
            },
            link: function(scope, iElement, iAttrs) {
                // create the svg to contain our visualization
                var svg = d3.select(iElement[0])
                    .append("svg")
                    .attr("width", "100%");

                // make the visualization responsive by watching for changes in window size
                window.onresize = function() {
                    return scope.$apply();
                };
                scope.$watch(function() {
                    return angular.element(window)[0].innerWidth;
                }, function() {
                    return scope.render(scope.data);
                });

                // watch the data source for changes to dynamically update the visualization
                scope.$watch('data', function(newData, oldData) {
                    return scope.render(newData);
                }, true);

                scope.render = function(data) {
                    // clear out everything in the svg to render a fresh version
                    svg.selectAll("*").remove();

                    // set up variables
                    var width, height, max;
                    width = d3.select(iElement[0])[0][0].offsetWidth;
                    height = scope.data.length * 40;
                    max = 100;
                    svg.attr('height', height);

                    // SVG rectangles for the bar chart
                    svg.selectAll("rect")
                        .data(data)
                        .enter()
                        .append("rect")
                        // color for bars. set to random. It can be changed to other gradients or solid colors.
                        .style({
                            fill: rColor
                        })
                        // set initial dimensions of the bar
                        .attr("height", 30)
                        .attr("width", 0)
                        // position bar
                        .attr("x", 10)
                        .attr("y", function(d, i) {
                            return i * 35;
                        })
                        // set up transition animations
                        .transition()
                        .duration(1250)
                        // finally, set the width of the bar based on the datapoint
                        .attr("width", function(d) {
                            return d.score / (max / width);
                        });

                    // add labels to the chart
                    svg.selectAll("text")
                        .data(data)
                        .enter()
                        .append("text")
                        .attr("fill", "white")
                        // position labels over their bars
                        .attr("y", function(d, i) {
                            return i * 35 + 22;
                        })
                        .attr("x", 15)
                        // get the label text from the datapoint
                        .text(function(d) {
                            return d[scope.label];
                        });
                };
            }
        };
    }]);

Create your ng-controller with initial data.

// Controller with initial data
angular.module('d3App.controllers')
    .controller('barchartController', ['$scope', function($scope) {
        $scope.title = "Tivix Oregon Leaderboard";
        $scope.d3Data = [{
            name: "Peter",
            score: 50
        }, {
            name: "Francis",
            score: 80
        }, {
            name: "Tabatha",
            score: 79
        }, {
            name: "Amber",
            score: 55
        }, {
            name: "David",
            score: 48
        }];
    }]);

In your HTML, connect each input element to the model data using “ng-model”. When the user input changes, Angular JS will re-render the D3.js visualization with the new data.

<!-- // call the app -->
<body ng-app="d3App">

<!-- // insert the controller -->
    <div  ng-controller="barchartController">

        <div class="title">{{title}}</div>

        <!-- // add the input boxes to make interactive -->
        <d3-bars data="d3Data" label="name"></d3-bars>

        <br>

        <span>Peter</span>
        <br>
        <input type="text" ng-model="d3Data[0].score"/>
        <br>

        <span>Francis</span>
        <br>
        <input type="text" ng-model="d3Data[1].score"/>
        <br>

        <span>Tabatha</span>
        <br>
        <input type="text" ng-model="d3Data[2].score"/>
        <br>

        <span>Amber</span>
        <br>
        <input type="text" ng-model="d3Data[3].score"/>
        <br>

        <span>David</span>
        <br>
        <input type="text" ng-model="d3Data[4].score"/>
      </div>

    </div>

</body>

And that’s it! Integration in this way is straightforward, especially if you’re familiar with Angular JS.

Here’s a working demo with some example styling (try changing the numbers in the input box!) :  

What do you notice?

You get the superior front-end experience of Angular JS and a responsive chart! D3.js takes care of making the charts and Angular JS takes care of the two-way data binding. Now, style this to your taste and you have a pretty cool, interactive visualization!

Now that you’ve seen a taste of these libraries together, where can you find more information?

This post should give you enough to do something similar, but try these tutorials for a more comprehensive look at what D3.js can accomplish and more information on pairing D3 with Angular JS: