Subscribe via RSS Feed

Simple Bar Graph in Angular Directive with d3.js and Prototype.js – Element based Directive Definition

July 11, 2013 3 Comments

AngularD3BarGraph

We are recently working to convert our client side javascript code to  convert in Object Oriented approach.

So we have gone through learning process of javascript OOP and written our previous article -

Javascript in OOP Way – some interesting study and while learning the d3.js graph creation, we have posted

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

We have separated  the d3.js code to put in Javascript Class and for that we have taken the prototype.js to make the javascript classes for the d3.js code separation. We have got this idea from one of our fellow blog commenter in d3.js article.

So - Simple Bar Graph with d3.js and Prototype.js – a Javascript Class based Approach is one of our previous article.

The current article is a variation of Angular Directive – it is a Element based Directive.

Example - In HTML we want to introdroduce new Tag Element like <bargraph></bargraph>.

We have made the Angular Directive and seperated the d3.js related code through class creation in prototype.js and accessed the the class instance related code in Angular Directive Complie Functon.

So what we have done really in HTML ?

1> Added the angular.js,prototype.js, d3.js in the HTML Page and the reference of the d3 related javascript class

 <script type="text/javascript" src="js/prototype.js"></script>
 <script src="js/barGraphProto.js"></script>
 <script src="http://d3js.org/d3.v3.min.js"></script>
 <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js">
 </script>

2> Put the the d3.js related Angular Directive in HTML -

<bargraph id="d3bar" 
          datajson="sample.json" 
          xaxis-name = "'Year'" 
          xaxis-pos = "905" 
          yaxis-name = "'Frequency'" 
          yaxis-pos = "6" 
          d3-format= "'.0%'" />
   <!-- angular directive is bargraph -->
   <!-- here "d3bar" is the id of bargraph tag directive variables are xaxis-name,yaxis-name,xaxis-pos,yaxis-pos,
        yaxis data format(d3-format)
        and finally the datajson variable which is holding the external data source -->

3> The Code in Angular Directive -

angular.module('AngularD3BarGraph', []) // Angular Module Name
    .directive('bargraph', function () { // Angular Directive
        return {
            restrict: 'E', // Directive Scope is Element
            replace: true, // replace original markup with template 
            transclude: false, // not to copy original HTML DOM
            compile: function (elem, attrs) { // the compilation of DOM is done here.
                // It is responsible for produce HTML DOM or it returns a combined link function
                // Further Docuumentation on this - http://docs.angularjs.org/guide/directive
                var html = "<div id='" + attrs.id + "' ></div>"; // the HTML to be produced
                var newElem = $(html);
                elem.replaceWith(newElem); // Replacement of the element.
                var ourGraph = new BarGraph(attrs.datajson,attrs.xaxisName,attrs.xaxisPos,attrs.yaxisName,
                    attrs.yaxisPos,attrs.d3Format);
                    //d3 related Variable initialisation              
                ourGraph.workOnElement('#'+attrs.id);
                // Work on particular element
                ourGraph.generateGraph();  // generate the actual bar graph
       }
    }
});

4> The d3 related bar graph code -

// properties are directly passed to `create` method
var BarGraph = Class.create({ // prototype class creation
    initialize: function(datajson,xaxisName,xaxisPos,yaxisName,yaxisPos,d3Format) {
                    this.datajson = datajson;
                    this.xaxisName = xaxisName;
                    this.xaxisPos = xaxisPos;
                    this.yaxisName = yaxisName;
                    this.yaxisPos = yaxisPos;
                    this.d3Format = d3Format;
                    // Taking all the variables and set in Class Instance Variables (d3 property specific)
                },
    workOnElement: function(element) {
                    this.element = element;
                    // Taking the DOM Element on which the graph will be drawn 
                },
    generateGraph: function() {

                   //d3 specific coding
                   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(this.d3Format);//setting the instance variable to d3 property

                  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(this.element).append("svg")
                          .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(this.datajson, function(error, data) { // getting the data
                          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", this.xaxisPos)
                         .attr("dx", ".71em")
                         .style("text-anchor", "end")
                         .text(this.xaxisName);

                 svg.append("g")
                         .attr("class", "y axis")
                         .call(yAxis)
                         .append("text")
                         .attr("transform", "rotate(-90)")
                         .attr("y", this.yaxisPos)
                         .attr("dy", ".71em")
                         .style("text-anchor", "end")
                         .text(this.yaxisName);

                 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); });
            }.bind(this)); // the bind syntax is important. Otherwise class instance variables will not be
                           // accessible in inner function 
         }
    });

Attached is the code for this for reader’s interest.

We will reuse the d3 related javascript class code in Angular.js Directive in our next post. So keep reading and add us in Google+ and/or tweet us in Twitter.

Enter your email address:

Delivered by FeedBurner

javascriptnosqlbook