Subscribe via RSS Feed

D3.js in Angular.js Directive – a relief from D3 svg code in HTML Page

July 2, 2013 12 Comments

We have came through D3.js coding for Graph generation in our projects. To me personally, this coding is somewhat complex for startup programmers.

Recently I am going through learning Angular.js and Angular Directive.  So I had a thought to introduce an angular directive which will wrap the complex functionality of D3.js.

So here is the result – this post – creating a bar chart through Angular Directive with the help of D3.js.

I have learned the D3.js code from – http://bl.ocks.org/mbostock/3885304

Also here is our previous documentation on making the Angular Directive.

First – the Bar Chart Picture –

AngularD3BarGraph

Now the Commented code snippet in HTML –

<body>
   <div id="d3bar" angulard3-bar-graph datajson="'sample.json'" 
                   xaxis-name = "'Year'" 
                   xaxis-pos = "905"  
                   yaxis-name = "'Frequency'" 
                   yaxis-pos = "6" 
                   d3-format= "'.0%'" >
   <!-- angular directive is angulard3-bar-graph -->
   <!-- directive variables are xaxis-name,yaxis-name,xaxis-pos,yaxis-pos,y axis data format(d3-format)
        and finally the datajson variable which is holding the external data source -->
   </div>
</body>

And below is

Angular directive which wrapped d3.js code for Bar Graph

javascriptnosqlbook

angular.module('AngularD3BarGraph', []) // Angular Module Name
    .directive('angulard3BarGraph', function () { // Angular Directive
        return {
            restrict: 'A', // Directive Scope is Attribute
            scope: {
                datajson: '=',
                xaxisName: '=',
                xaxisPos: '=',
                yaxisName: '=',
                yaxisPos: '=',
                d3Format: '='
                // All the Angular Directive Vaiables used as d3.js parameters
            },
            link: function (scope, elem, attrs) {
                // The d3.js code for generation of bar graph. further reading should be done from http://d3js.org/
                var margin = {top: 20, right: 20, bottom: 30, left: 40},
                    width = 960 - margin.left - margin.right,
                    height = 500 - margin.top - margin.bottom;

                var formatPercent = d3.format(scope.d3Format); // formatting via angular variable

                var x = d3.scale.ordinal()
                       .rangeRoundBands([0, width], .1);

               var y = d3.scale.linear()
                       .range([height, 0]);

               var xAxis = d3.svg.axis()
                       .scale(x)
                       .orient("bottom");

               var yAxis = d3.svg.axis()
                       .scale(y)
                       .orient("left")
                       .tickFormat(formatPercent);

               var svg = d3.select("#"+elem[0].id).append("svg") // selecting the DOM element by d3.js 
                                                                 // - getting from Angular context   
                   .attr("width", width + margin.left + margin.right)
                   .attr("height", height + margin.top + margin.bottom)
                   .append("g")
                       .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

              d3.json(scope.datajson, function(error, data) { // external data filename- angular directive variable
                  if (error) return console.warn(error);

                  x.domain(data.map(function(d) { return d.letter; }));
                  y.domain([0, d3.max(data, function(d) { return d.frequency; })]);

                  svg.append("g")
                      .attr("class", "x axis")
                      .attr("transform", "translate(0," + height + ")")
                      .call(xAxis)
                      .append("text")
                          .attr("x", scope.xaxisPos)
                          .attr("dx", ".71em")
                          .style("text-anchor", "end")
                          .text(scope.xaxisName);
                  // x axis legend setting from angular variables
                  svg.append("g")
                      .attr("class", "y axis")
                      .call(yAxis)
                      .append("text")
                          .attr("transform", "rotate(-90)")
                          .attr("y", scope.yaxisPos)
                          .attr("dy", ".71em")
                          .style("text-anchor", "end")
                          .text(scope.yaxisName);
                  // y axis legend setting from angular variables
                  svg.selectAll(".bar")
                      .data(data)
                      .enter().append("rect")
                      .attr("class", "bar")
                      .attr("x", function(d) { return x(d.letter); })
                      .attr("width", x.rangeBand())
                      .attr("y", function(d) { return y(d.frequency); })
                      .attr("height", function(d) { return height - y(d.frequency); });
                  });
              }
           }
       });

So that is all for now.

Code can be downloaded from Github.

As we are learning d3.js, we will explore wrapping more of the functional code in angular directive in our later works.

Thanks for reading…  Also if you find this article helpful, you can connect us in Google+ and Twitter.

Want to get blog updates? Subscribe here
[newsletter_form]

Comments (12)

Trackback URL | Comments RSS Feed

  1. janeklb says:

    Thanks for posting! Just a tip: you could improve the code a bit by using xaxis-name = “Year” rather than xaxis-name = “‘Year'” if you declare your directive scope using ‘@’ rather than ‘=’ for all attributes that are meant to be read as literal values (strings).

  2. thegreenpizza says:

    The correct way to do this is by externalizing the D3 charts as Javascript classes (objects with prototypical inheritance) and then perform attrs.$observe on data that will change the D3 chart. If you use this pattern you can keep the chart out of the Angular code but still keep it working in tandem, making it easier to maintain. Using JS classes also allows you to extend charts (I have a bar chart class, and a stacked bar chart class that extends the first).

    Attrs.$observe also fixes the issue of the data not being available immediately in the link function.

Leave a Reply

You must be logged in to post a comment.