Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular ng-click's inside a dynamically created d3 chart are not working

I'm trying to create a d3 chart using an Angular directive. I manage to create it, but the problem is that I want some ng-click events on the chart elements and I'm not really sure how this should be done.

Here is my directive:

.directive('circleChart', function($parse, $window, $compile) {
        return {
            restrict: 'A',
            scope: {
                datajson: '='
            },
            link: function(scope, elem, attrs) {

                var circleChart = new CircleChart(scope.datajson);
                circleChart.initialise(scope);
                var svg = circleChart.generateGraph();
                svg = angular.element(svg);

                console.log(svg);
                //scope.$apply(function() {
                  var content = $compile(svg)(scope);
                  elem.append(content);
                //});
            }
        }
    });

The CircleChart object creates my d3 chart and there is the place I attach an ng-click attribute to the chart elements (doesn't seem to be a proper Angular way of doing it):

var CircleChart = Class.create({
    initialise: function(scope) {
        this.datajson = scope.datajson;
    },

    generateGraph: function() {

    .............

    var chartContent = d3.select("div#chart-content");

    var svg = chartContent.append("svg")
        .attr("id", "circle")
        .attr("width", diameter)
        .attr("height", diameter)
        .style("border-radius", "50px")
        .append("g")
        .attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");

      ...............

           var circle = svg.selectAll("circle")
                .data(nodes)
                .enter().append("circle")
                .attr("class", function(d) {
                    return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root";
                })
                .attr("id", function(d) {
                    return d.id;
                })
                .attr("value", function(d) {
                    return d.name
                })
                .attr("parent", function(d) {
                    if (d.parent) return d.parent['id']
                })
                .attr("ng-click", function(d) {
                    return "getEgs('" + d.id + "')";
                })

    ...............

The d3 code is working fine because if I remove the compile code from the directive, the chart gets drawn, but the ng-clicks don't fire.

If I leave the compile code there, it goes into an infinite loop and the browser is building up memory until I have to kill it.

Obviously, my method is not working and it will be awesome if someone can help me out

like image 650
Razvan Ilin Avatar asked May 05 '15 14:05

Razvan Ilin


1 Answers

Since you are modifying the DOM from inside the CircleChart.generateGraph and re-appending the contents of the element in you directive: elem.append(content) after compilation, you (probably) end up in an infinite loop.

If you do want to compile the template thus produced, you can simple call $compile(elem.contents())(scope); without having to clear and append the content again. $compile will work Angular magic on the DOM in-place.


However, as I suggested in the comments, since you have access to the whole scope in CircleChart (which in my opinion is probably an overkill), you should capture the click event in d3 and invoke the getErgs function from code instead of $compile-ing the code and delegating the task to Angular.

like image 172
musically_ut Avatar answered Sep 27 '22 19:09

musically_ut