21
votes

I'm going to need some basic charts for one of my apps, but I would like to use D3JS if I'm able to wrap my head around it in time to fulfill the project requirements. I'm still developing my understanding of SVG and D3JS so I can use it effectively, so far I've been able to make a very basic bar chart that takes 2 dimensional arrays and shows bar charts based on the length of each array in the top level array. This works pretty darn well (though it could use some decoration/axis labels etc.). The next kind of chart I was going to work on is a pie chart since these are very common as well.

Basically what I'm wondering is, does anyone know if someone has already done this and posted to github (or shared the source elsewhere) so I don't have to start from scratch here. I realize D3JS is used for doing very custom data display but I really just want it for the basics plus the ability to customize. So is anyone aware of an effort to create directives for D3JS and/or anyone aware of somewhere that outlines all the basic chart types in D3JS (I keep finding complex examples, which look amazing but I fear I won't understand/learn from them).

Basically I would just like to have an easy way to drop in (and then style) the following charts: bar, line, pie (other standard chart types I'm not thinking of are welcome). Also I have seen the Google Charts and High Charts options, which are both nice and give you a bit of this out of the box, but I prefer a build up approach rather than a strip down most of the time.

Also I'm aware of and used this article to make the original bar graph I needed (mixing it with another straight D3JS tutorial) but are there more efforts anyone is aware of?

Here's my basic bar chart so far:

.directive('barChart', function ( /* dependencies */ ) {
  // define constants and helpers used for the directive

  var width = 500,
    height = 80;

  return {
    restrict: 'E', // the directive can be invoked only by using <bar-chart></bar-chart> tag in the template
    scope: { // attributes bound to the scope of the directive
      val: '='
    },
    link: function (scope, element, attrs) {
      // initialization, done once per my-directive tag in template. If my-directive is within an
      // ng-repeat-ed template then it will be called every time ngRepeat creates a new copy of the template.

      // set up initial svg object
      var vis = d3.select(element[0])
        .append("svg")
          .attr("class", "chart")
          .attr("width", width)
          .attr("height", height);


      // whenever the bound 'exp' expression changes, execute this 
      scope.$watch('val', function (newVal, oldVal) {

        // clear the elements inside of the directive
        vis.selectAll('*').remove();

        // if 'val' is undefined, exit
        if (!newVal) {
          return;
        }

        var totalDataSetSize = 0;

        for (var i = 0; i < newVal.length; i++) {
          totalDataSetSize += newVal[i].length
        };

        function calcBarWidth(d) {
          return (totalDataSetSize==0)?0:d.length/totalDataSetSize*420;
        }

        vis.selectAll("rect")
            .data(newVal)
          .enter().append("rect")
            .on("click", function(d,i) {alert("Total gardens: "+ d.length)})
            .attr("y", function(d, i) { return i*20; })
            .attr("width", calcBarWidth)
            .attr("height", function(d) {return 20});

        vis.selectAll("text")
            .data(newVal)
          .enter().append("text")
            .attr("x", function(d) { return calcBarWidth(d)+50})
            .attr("y", function(d, i) { return (i+1)*20; })
            .attr("dx", -3) // padding-right
            .attr("dy", "-.3em") // vertical-align: middle
            .style("font-size", ".7em")
            .attr("fill", "black")
            .attr("text-anchor", "end") // text-align: right
            .text(function(d,i){ var yesOrNo = i?" No":" Yes"; return d.length.toString() + yesOrNo})

      },true);
    }
  };
});

Any tips/pointers on this code are welcome as well, as I said still a complete novice with D3JS and still fairly new to Angular.

5
Not angular directives, but NVD3 includes a lot of basic and more advanced chart types that you may be able to wrap in such directives.Lars Kotthoff
Check out angular-nvd3 directive for nvD3.js. This directive is re-usable and can interact with full nvd3 api via JSON.krispo

5 Answers

4
votes

Hmm okay apparently my initial research wasn't that good, I just came upon this which looks like it covers what I want:

http://phloxblog.in/angulard3/start.html#.Ui9XPBKJB-M

Still if there are alternatives I'm open to hearing/seeing them as well. If I don't see any better responses in the next day I'll accept this answer.

-------------------------- Edit 1

Also if anyone knows why prototype is being used here I'd like to know I'm going to try to remove the dependency on it since I'd rather not introduce more code if I don't need it.

-------------------------- Edit 2

Reading up on the topic more there are some other samples which also have a Class setup for the purpose of abstracting/decoupling the D3 code from the AngularJS code (to allow for extension is one argument I've seen).

http://bl.ocks.org/biovisualize/5372077

4
votes

I've built a set of extensible directives for using D3 to build charts. They are quite flexible and I already use them extensively. You can get the package from Bower as well as "angularD3".

The basic idea is to construct charts declaratively while still allowing access to the more powerful aspects of D3 and providing extensibility for further development of custom directives.

The root directive <d3-chart> acts as a container and controller for the various chart components such as axis, lines, areas, arcs, gradients, etc. The d3ChartController is allows you to write your own custom chart directives easily. We use this ourselves for some specialized custom labels and such.

Here is a sample of how things are declared:

 <d3-data src="data/data.csv" data="line" columns="year, savings, total, optimal"></d3-data>
 <d3-data src="data/donutData.csv" data="pie" columns="age,population"></d3-data>
 <div d3-chart>
   <d3-axis data="line" name="year" label="Year" extent="true" orientation="bottom" ticks="5"></d3-axis>
   <d3-axis data="line" name="savings" label="Deposits" orientation="left" ticks="5"></d3-axis>
   <d3-axis data="line" name="total" label="Savings" orientation="right" ticks="5"></d3-axis>
   <d3-line data="line" x="year" y="optimal" yscale="total"></d3-line>
 </div>
2
votes

There's also dangle.js. It's supposed to be for elasticsearch results, but frankly it's robust enough that you can use it for many other use cases.

0
votes

I would be nice if AngularJS could integrate d3 like they did with bootstrap. There is really nothing new on this topic (angular & d3). Most of the d3 directives (except nvd3) are two years old. Here is another article from 2013 but the idea is more sustainable. You use the newest d3 for the calculations only. And you use native angular for the dom manipulation. The drawback is, that I was not able to get transitions running by this approach.