Question

I have interesting question regarding an application making use of AngularJS and D3. Essentially, I have an ng-repeat which repeats based on the contents of an array. In this array are objects which contain a REST url they will periodically reach out to and pull data from. Once they get this data, a graph is drawn using D3 (one graph for each object in the array).

The issue I am having is dynamically adding an object to this array via user input, and having the drawing work correctly. It appears that the d3 code jumps the gun and starts trying to select SVG elements that haven't been rendered yet as Angular is still finishing adding stuff to the DOM via ng-repeat.

I have tried using $timeout, and also created a custom directive similar to this: ng-repeat finish event.

It works well, but only seems to fire the event the first time that the ng-repeat is run, not when I add another object to the array. Any comments on the best way to handle this situation? If I could fire the event based on $last everytime a new object is added/removed and then iterate through the array and invoke the drawing function, that seems like it would work.

Cheers!

Edit: The D3 code currently is similar to this http://bl.ocks.org/mbostock/1346410 which is just a simple donut chart update, with the caveat that the redraw is triggered by a REST call. In the future however there may be more complicated graphs and such which is why I was hoping to use D3.

Edit 2: I have moved all my D3 code into a custom directive and after a lot of messing around it seems to work almost as it should. By not isolating the scope, the custom element has the same scope as the ng-repeat and can access the object and its properties, as well as the relevant DOM elements (currently I am creating a DIV with an ID that is unique to each object).

The problem now comes when using d3.select...when I try and select the DIV where I want to place the drawing, it will not allow me to select by ID, as it returns null..what is very strange about this is if I selectAll("div"), it can find the div I'm looking for, but won't let me select just that one.

Here is a JsFiddle showing what I mean...check the console and see the first test returns the div "obj1", but the second select returns null.

If I could get this working and then append my SVG canvas and drawing within that div, I think it would work!

Relevant selects:

    var test = d3.selectAll("div");
       console.log(test);
       var chart = d3.select("#obj" +obj.id);
       console.log(chart);

HTML that should create the divs:

    <div ng-repeat="object in myData">
        <div id="obj{{object.id}}">
            Hi 
           <bars-chart></bars-chart>
       </div>
   </div> 

http://jsfiddle.net/SleepyProgrammer/eN6p7/3/

Was it helpful?

Solution

There's no need to use the ID for selection at all. You can simply select the element[0] passed in to the Angular directive's link function:

var chart = d3.select(element[0]).append('svg')

For example:

angular.module('myApp', []).
   directive('barsChart', function () {
     return {
         restrict: 'E',
         link: function (scope, element, attrs) {
             var chart = d3.select(element[0]).append('svg').attr({height: 20, width: 20});             
             chart.append('rect').attr({height: 10, width: 10});             
         } 
      };
   });

function Ctrl($scope, $timeout) {
    $scope.myData = [ {"id":"1"}, {"id":"2"}];

    // just to show that it reacts to changes in the data and will render more SVGs
    $timeout(function() {
        $scope.myData.push({"id":"3"});
    }, 3000);    
}

See JSFiddle here:

http://jsfiddle.net/4vdvq/2/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top