A pretty good answer from @jbll - But it will probably be best to chain the directive compilation onto the end of the enter phase. It is important to have an enter phase and an update phase so the graphic can respond to data updates without recreating every element. The previous answer would have every directive on every node compiled whenever the model was changed. This may be what is wanted, but probably not.
The following code shows the d3 graphic updating whenever the $scope.nodes variable changes.
This is also a little neater because it doesn't require the removal and recreation of the original directive, which seems like a bit of a hack.
Here is the Fiddle
Add the button to the html:
<button ng-click="moveDots()">Move the dots</button>
And then change the JavaScript fie to:
var myApp = angular.module('myApp', ['ui.bootstrap']);
myApp.controller('myCtrl', ['$scope', function($scope){
$scope.nodes = [
{"name": "foo", x: 50, y: 50},
{"name": "bar", x: 100, y: 100}
];
$scope.moveDots = function(){
for(var n = 0; n < $scope.nodes.length; n++){
var node = $scope.nodes[n];
node.x = Math.random() * 200 + 20;
node.y = Math.random() * 200 + 20;
}
}
}]);
myApp.directive('myNodes', ['$compile', function ($compile) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var mySvg = d3.select(element[0])
.append("svg")
.attr("width", 250)
.attr("height", 250);
renderDots();
scope.$watch("nodes", renderDots, true);
function renderDots(){
// ENTER PHASE
mySvg.selectAll("circle")
.data(scope.nodes)
.enter()
.append("circle")
.attr("tooltip-append-to-body", true)
.attr("tooltip", function(d){
return d.name;
})
.call(function(){
$compile(this[0].parentNode)(scope);
});
// UPDATE PHASE - no call to enter(nodes) so all circles are selected
mySvg.selectAll("circle")
.attr("cx", function(d,i){
return d.x;
})
.attr("cy", function(d,i){
return d.y;
})
.attr("r", 10);
// todo: EXIT PHASE (remove any elements with deleted data)
}
}
};
}]);