afeffifari-1957 afeffifari-1957 - 6 months ago 100
AngularJS Question

Display informations about a bubble chart D3.js in AngularJS

I am searching how to display information about a bubble D3.js in AngularJS. I can show an alert with the desired information but I can't display informations on the page with AngularJS because I have no access to the $scope because my graphe is in a directive...
How can I pass the informations without change the structure of the web app ? I can't put the formation the bubble chart outsite of this directive.
This is my HTML :

<body ng-app="d3DemoApp">
<div id="graph1" ng-controller="controllerBubble">
The package name should appear here : {{packageName}}
<bubble-chart chart-data="chartData"></bubble-chart>
</div>
</body>


The service :

d3DemoApp.service('dataService', function AppCtrl($http, $q) {
this.getCommitData = function(param) {
var deferred = $q.defer();
$http({
method: 'GET',
url: param
}).
success(function(data) {
deferred.resolve({
chartData: data,
error: ''
});
}).
error(function(data, status) {
deferred.resolve({
error: status
});
});
return deferred.promise;
};
});


The controller :

var d3DemoApp = angular.module('d3DemoApp', []);

// controller business logic
d3DemoApp.controller('controllerBubble', function AppCtrl($rootScope, $scope, dataService) {

$scope.choice = 'data.json';

loadData($scope.choice);

function loadData(param) {
dataService.getCommitData(param).then(function(res) {
if (res.error) {
$scope.error = res.error;
return false;
}
$scope.chartData = res.chartData;
});
}

});


d3DemoApp.directive('bubbleChart', function() {
return {
restrict: 'EA',
transclude: true,
scope: {
chartData: '='
},
link: function(scope, elem, attrs) {

scope.$watch('chartData', function(newValue, oldValue) {
console.info('new data comes to directive');
console.info(newValue);
if (newValue) {
scope.drawChart(newValue);
}
});

scope.drawChart = function(rootData) {

var diameter = 900,
format = d3.format(",d"),
color = d3.scale.category20c();

var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.value(function(d) {
return (d.numberOfLink + 1);
})
.padding(1.5);

var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");

var filt = svg.append("defs")
.append("filter")
.attr({
id: "f1",
x: 0,
y: 0,
width: "200%",
height: "200%"
});
filt.append("feOffset").attr({
result: "offOut",
"in": "sourceAlpha",
dx: 10,
dy: 10
});
filt.append("feGaussianBlur").attr({
result: "blurOut",
"in": "offOut",
stdDeviation: 10
});
var feMerge = filt.append("feMerge");
feMerge.append("feMergeNode").attr("in", "offsetBlur")
feMerge.append("feMergeNode").attr("in", "SourceGraphic");

var node = svg.selectAll(".node")
.data(bubble.nodes(classes(rootData))
.filter(function(d) {
return !d.children;
}))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});

node.append("title")
.text(function(d) {
return d.className + ": " + format(d.value);
});

node.append("circle")
.attr("r", function(d) {
return d.r;
})
.style("fill", function(d) {
return "red";
});
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) {
return d.className.substring(0, d.r / 3);
})

node.on("click", click);
function click(d) {
alert(d.packageName);
$scope.packageName = d.packageName; // How to access to the scope ?
}

// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];

function recurse(name, node) {
if (node.children) node.children.forEach(function(child) {
recurse(node.name, child);
});
else classes.push({
packageName: name,
className: node.name,
value: node.numberOfLink,
idProjet: node.projectId,
numberOfLink: node.numberOfLink,
priority: node.priority
});
}

recurse(null, root);
return {
children: classes
};
}
d3.select(self.frameElement).style("height", diameter + "px");
}


if (typeof scope.chartData != "undefined") {
scope.drawChart(scope.chartData);
}
}
};
});


This is an online example of the problem with Plunker : https://plnkr.co/edit/LUa7RHxjSaVe1KTzy33c?p=preview

Hope someone will can make works the Plunker ! Thanks.

Answer

Here's the result: https://plnkr.co/edit/CnoTA0kyW7hWWjI6DspS?p=preview

You have to create a shared service/factory between your controllers in order to do that. There are lots of examples out there.

angular.module('d3DemoApp').factory('NotifyingService', function($rootScope) {
    return {
        subscribe: function(scope, callback) {
            var handler = $rootScope.$on('notifying-service-event', callback);
            scope.$on('$destroy', handler);
        },

        notify: function(msg) {
            $rootScope.$emit('notifying-service-event', msg.packageName);
        }
    };
});
Comments