简体   繁体   中英

Integrating d3 with a custom angular directive

I am working on an application that uses angular to keep data coming in from a few different services in sync and to hand this data off to some d3 functions to generate graphics and tables.

Right now, I'm having difficulty getting the data bound at the correct level within the d3 code. I'm creating a basic table at this point.

Here is my directive:

application.directive('d3table', function() {
   var chart = table();
   return  {
       restrict: 'E',
       scope: {
        data: "="
       },
       link: function(scope, element, attrs) {
           scope.$watchCollection('data', function(data) {
               d3.select(element[0]).datum(data).call(chart);
           });
       }
   }
});

My d3 code for table() is as follows, using the toward reusable charts approach:

function tableChart() {
    function chart(selection) {
        selection.each(function(dataset) {
            var table = d3.select(this).append('table');

            table.append('thead').append('tr')
              .selectAll('tr').data(columns).enter()
              .append('th').text(function(d) { return d;});
    }
    return chart;
}

The data I am working here is a simple array. Data is added and removed periodically.

The issue I am having is that instead of updating the existing table, the appending of data is triggering the generation of a new table. So I end up with multiple tables, instead of one which is properly bound to the d3 function.

You could remove the table beforehand.

function tableChart() {
    function chart(selection) {
        selection.each(function(dataset) {
            //remove existing table
            d3.select(this).select("table").remove();

            var table = d3.select(this).append('table');

            table.append('thead').append('tr')
              .selectAll('tr').data(columns).enter()
              .append('th').text(function(d) { return d;});
    }
    return chart;
}

I Would use a template and just basically have your table and table head info in there then in the link function you can get a reference to the table. Then in the watch collection function your only adding or removing rows, not trying to recreate the whole table each time. This approach will work as long as your data scheme stays the same.

Here is some code to look at

//js
var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.name = 'D3 Angular Table';

  $scope.data = [1,2,3,4,5,6,7,8];


  $scope.add = function(){
    console.log('add');
    $scope.data.push(Math.floor(Math.random() * 100) );
  };

   $scope.remove = function(){
    console.log("remove")
     $scope.data.splice(0, 1)
  };
});




app.directive('d3table', function() {
   //var chart = table();
   return  {
       restrict: 'E',
       scope: {
        data: "="
       },
      templateUrl: 'd3table.html',
       link: function(scope, element, attrs) {

           var table = element[0].firstChild;

           scope.$watchCollection('data', function(data) {

             var selectedTable = d3.select(table).selectAll('tr').data(data);

              selectedTable.enter()
              .append("tr")
              .text(function(d){return d});

              selectedTable.exit().remove();

           });
       }
   }
});
//html
<table id = "test" >
  <thead>
     <tr><th>Value</th></tr>
  </thead>
</table>

working plunk

http://plnkr.co/edit/hb6t9gshXBngApUGyJkN?p=preview

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM