What is a d3 Angular component?
D3.js is a cool JavaScript library for manipulating documents based on data. It is a collection of helper functions that makes working with DOM and DATA very pleasant. Why do we need Angular than? Because D3 is not a plug and play charts library like google charts. But by creating our own d3 components we can have our own encapsulated and reuseable visualizations.
We can take advantage of flexibility of d3 to make our visualizations as we like them and use angular to include interactivity.
“The Angular way” of integrating D3?
D3 logic in a directive
Use HTML-declarative syntax to feed of data to your directive instances
Store the data in your controller
Create Angular filters using D3 methods.
Let's talk about the angular way of integrating d3.
All your D3 logic and presentation must be contained within a directive
Use HTML-declarative syntax to feed of data to your directive instances
By doing that, you can store the data in your controller, passing it to your D3 directive via data binding of parameters.
Another cool use of Angular and d3 is creating your custom filters.
You can take advantage of d3's formatting method's and use it easily anywhere in your application.
How we use it in html
We can declartively use our prepared components with any data anywhere.
this is our interface for angular AAANNNND angular 2!!!
//Find gifs
Or it can look like this. You can bind isolated scope properties to any DOM attribute to customize your graph. I think this is a very powerful tool.
We can use other angular directives like ng-repeat to make a lot of the graphs.
how we define Angular 1
angular.module('App', [])
.directive('graph', function() {
return {
restrict: 'E',
scope: {
data: '=bindData', // bindings
width: '@width', // static value
color: '&color' // expression
},
link: Graph
};
function Graph(scope, element, attrs) { // d3 code
};
});
JsFiddle
Directives have a link function which is essentially a “constructor”.
It contains your d3 code that assumes some sort of data but doesn't know about your data.
Scope options: The & binding allows a directive to trigger evaluation of an expression in the context of the original scope, at a specific time.
Enter and Exit
scope.$watch('data', function(data){
if (!data) return;
//divs selection with the data
divs = divs.data(data);
//remove the extra elements
divs.exit().remove();
divs.enter().append('div')
.transition().ease("elastic")
.style("width", function (d) {
return d + "%";
})
.text(function (d) {
return d + "%";
});
}, true);
Enter and exit is one of the most confusing concepts in d3.
$watch is responsible for adding or removing the necessary div tags and combination with d3’s .enter() .exit() selections.
Getting Data
d3.json('data.json', function(err, data){
//render the graph with the data
});
d3.csv('data.csv', function(err, data){...})
D3 comes with a set of useful utilities for loading data from remote sources. The most common being the d3.json() or d3.csv()‘ functions. If used properly, we can continue to use
these same functions but Angular also offers its own alternatives for loading in data.
Our directive “know” nothing about where it got its data and only care about if or when it gets data and when that data changes.
Using $http service instead of d3.json() allows us to avoid having to call $scope.$apply() directly.
$http.get('/yourApiUrl').
success(function(response) {
// when the response is available
$scope.data = response.data;
}).
error(function(response) {
});
To allow the above directive to not know about the data, we should only have it watch for changes to the data on the scope, and then we’ll load our data onto the scope from somewhere else. “someplace else” will be a controller. The controller is where the logic should go that ties together our application.
Truth about the data
Example
Array Functions
Data is never in the format that you want it to be.
There are couple of places you can deal with this. One of them is in your controller, you can manupulate your data. D3 has all the array methods that you need.
divs.style("width", function (d) {
return scope.accessor({d:d}) + "%";
})
Accessor JSFiddle
By making the accessors configurable, we avoid having a directive that assumes a particular input format. This means our directive can accept any type of data array so long as we tell it how to pluck the specific values out of the data using the accessor expression.
Using TypeScript
module graphs {
export function graph(): ng.IDirective {
return {
link: ($scope: ng.IScope, element: JQuery, attributes: any) => {
...construction function goes here.
}
};
}
}
Checkout definetly typed for Type definitions for Angular JS 1.3+
You can do as much or as little as you want.