I'm trying to create my first app with AngularJS. It looks neat, but there's a lot of abstraction, and I'm just curious if anyone has advice on the most idiomatic way to use the angular methodology to update visuals created with d3js.
Thanks, bp
D3 can load CSVs from your Angular application or a third-party URL and makes the data available as an array of objects in the resulting promise.
The JavaScript ecosystem has completely changed during this time, in terms of libraries, best practices and even language features. Nevertheless, D3 is still here. And it's more popular than ever.
Include D3 Library from CDN js library by linking it directly into our HTML page from the Content Delivery Network (CDN). CDN is a network of servers where files are hosted and are delivered to a user based on their geographic location. If we use the CDN, we do not need to download the source code.
In order to make angular and other frameworks play nice is to wrap the "other" frameworks using directives.
http://docs.angularjs.org/guide/directive
The thing that you want to do is to tell angular when data has been updated by the "other" frameworks. If angular doesn't need to know, then your task is simpler.
Here is an example that works with SVG, its awesome
http://sullerandras.github.com/SVG-Sequence-Diagram/
Here is an example that wraps TinyMCE
http://jsfiddle.net/programmieraffe/kjsEV/
There is also the possibility to insert the AngularJS handle bar syntax directly into the d3 generated elements:
var containerDiv = d3.select(targetCSSSelectorForADiv); var svgG = containerDiv .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 + ")") svgG.selectAll(".tempclass").data(scope.circles).enter() .append("circle") .attr("class", "tempclass") .attr("cx", function (d, i) { return "{{circles[" + i + "].cx}}" }) .attr("cy", function (d, i) { return "{{circles[" + i + "].cy}}" }) .attr("r", function (d, i) { return "{{circles[" + i + "].radius}}" }) .attr("ng-style", function (d, i) { return "{fill: circles[" + i + "].circolor" + ", opacity: circles[" + i + "].opa" + ", 'stroke-width': 4*circles[" + i + "].opa" + ", stroke: 'red' }"; });
Please note the following things: the scope is in-fact the angular scope object passed down from the directive to the rendering function. Setting the style of an element to an "{{...}}" expression will not work so I am using the "ng-style" attribute here.
However there is one more trick: You need to tell Angular to look at the dynamically generated DOM elements and wire up the data binding, I know now of two ways of doing this:
//the target div is the one with the angular ng-controller attribute //this you can call at the end of the d3 rendering call from within the render function angular.bootstrap(document.getElementById("d3ContainerDivID"), ['d3App']);
the other way is this:
//and this could be called from the directive that triggered the rendering or //some other place that could have the angular $compile service injected $compile(document.getElementById("d3ContainerDivID"))(scope);
Now you can change your scope members and they will be directly updated to your d3 elements, in this case the svg circles. In the angular controller (which gets instantiated before the directive fires that draws the d3 objects).
$scope.circles = []; for (var i = 0; i < 50; i++) { $scope.circles.push(new Circle()); } setInterval(function () { $scope.circles.forEach(function (d, i) { $scope.circles[i] = new Circle(); }); $scope.$digest(); }, 2000);
Please note the $digest call, which tells angular to digest the changed scope; this will change the values on the svg circle elements. For anything like animations and such, d3 is now not responsible anymore and one would have to implement manually or use a different pattern.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With