2015-12-05 15 views
1

我想一起使用d3和角度。我建立了以下d3模塊:

angular.module('DTBS.d3', []) 
    .factory('d3Service', ['$document', '$q', '$rootScope', 
    function($document, $q, $rootScope) { 
     var d = $q.defer(); 
     function onScriptLoad() { 
     // Load client in the browser 
     $rootScope.$apply(function() { d.resolve(window.d3); }); 
     } 
     // Create a script tag with d3 as the source 
     // and call our onScriptLoad callback when it 
     // has been loaded 
     var scriptTag = $document[0].createElement('script'); 
     scriptTag.type = 'text/javascript'; 
     scriptTag.async = true; 
     scriptTag.src = 'http://d3js.org/d3.v3.min.js'; 
     scriptTag.onreadystatechange = function() { 
     if (this.readyState == 'complete') onScriptLoad(); 
     } 
     scriptTag.onload = onScriptLoad; 

     var s = $document[0].getElementsByTagName('body')[0]; 
     s.appendChild(scriptTag); 

     return { 
     d3: function() { return d.promise; } 
     }; 
}]); 

angular.module('DTBS.directives', []) 
    .directive('d3Bars', ['d3Service', function (d3Service) { 
    return { 
     restrict: 'EA', 
     scope: {}, 
     link: function(scope, element, attrs) { 
     d3Service.d3().then(function(d3) { 
      // d3 code goes here 
      var svg = d3.select(element[0]) 
      .append("svg") 
      .style('width', '100%'); 

      scope.render = function (data) { 
      // remove all previous items before render 
      svg.selectAll('*').remove(); 
      // If we don't pass any data, return out of the element 
      if (!data) return; 
      svg.selectAll('rect') 
      .data(data).enter() 
      .append('rect') 
      .attr('height', 50) 
      .attr('width', 50) 
      .style('background-color', red) 
      }; 

      // set up watch to see if button clicked; add rectangle with render func 
      scope.$watch('data', function(newVals, oldVals) { 
      console.log("hey") 
      return scope.render(newVals); 
      }, true); 
     }); 
     }}; 
    }]); 

在我的代碼的主要部分中,我有一個名爲'DTBS.test'的模塊。這個模塊有一個表格控制器,它有一個叫'保存'的功能。我希望我的d3指令監視這個保存函數被調用,當它被調用時,應該調用渲染函數來爲svg添加一個矩形。

angular.module('DTBS.test', []) 
.controller('TableController', ['$scope', function ($scope) { 
    var secondsToWaitBeforeSave = 3; 
    $scope.table = {}; 
    //Table save function that clears form and pushes up to the parent 
    $scope.save = function() { 
     $scope.id++; 
     $scope.table.id = $scope.id; 
     $scope.table.attrs = []; 
     $scope.addTable($scope.table); 
     $scope.table = {}; 
    }; 

    }]) 

在是看更改爲「數據」,這是不存在的時刻 - 我的問題是什麼應該把它看的,以及如何在測試模塊的控制器事件鏈接到手錶設置d3模塊的指令?

回答

2

我將創建一箇中間數據服務,以便什麼都可以,如果這樣的傾向操縱D3的數據。該數據服務可以廣播事件,您的指令可以捕捉到重新渲染數據。

.service('d3Data', ['$rootScope', function($rootScope) { 
    var data = []; 
    var emit = function(data) { $rootScope.$broadcast('d3:new-data', data); } 
    var api = { 
    get: function() { 
     return data; 
    }, 
    set: function(data) { 
     data = data; 
     emit(data); 
     return data; 
    }, 
    push: function(datum) { 
     data.push(datum); 
     emit(data); 
     return data; 
    } 
    } 

    return api; 
}]) 

我已經創建了一些小的幫助函數,以防您需要它們。你的指令中不會有太多變化。只需添加一種方法來捕獲事件而不是watch。你可以把它放在你的scope.render函數的下面。

scope.$on('d3:new-data', function(e, data) { 
    scope.render(data); 
}); 

接下來,您將擁有想要與D3圖表交談的控制器。

.controller('TableController', ['$scope', 'd3Data', function ($scope, d3Data) { 
    $scope.table = {}; 
    //Table save function that clears form and pushes up to the parent 
    $scope.save = function(data) { 
    data = angular.copy(data); 
    d3Data.push(data); 
    }; 
}]) 

模板將通過NG-模型提供了save功能的數據。假設你會在你的模板中輸入一些ng模型,如table.widthtable.height,那麼對於你的提交按鈕,你可以把ng-click="save(table)"。然後,我們複製該數據,以便它不再與表單雙向綁定。並使用我們新的d3Data服務推送數據,該服務將廣播消息,指令將捕獲並更新數據。

我創建了一個簡單的Plunk來演示這一點。 http://plnkr.co/edit/VMRNvZ?p=preview

+0

的任何位置添加'scope.data = [// data here]',這正是我正在尋找並完美工作的地方!不能太感謝你了,太棒了 –

0

我看到的一個大問題是data變量僅在render函數中可用。您在render之外撥打$watch,其中dataundefined

一個簡單的解決方案將是如下:

scope.data; 
scope.render = function (data) { 
    scope.data = data; 
    // some code here 
} 

scope.$watch('data', function(newVals, oldVals) { 
} 
+0

我不同意這一點。最好將渲染作爲純函數(如原始示例所示)。讓他們的例子工作所需要做的唯一事情就是在鏈接函數 – kevrom