簡體   English   中英

如何將D3JS圖與Angular指令集成

[英]How to integrate `d3js` graph with angular directive

我正在嘗試將d3js圖形與我的angular指令集成在一起。 但我沒有得到預期的結果。

  1. 該圖未正確附加到div; 相反,我得到了undefined%標簽
  2. 每次我單擊列表(' li )時,不是更新圖形,而是附加一個新圖形。

請單擊頂部的列表。

這是我的代碼和演示:

var myApp = angular.module("myApp", ['ngResource']);

myApp.factory("server", function($resource) {

  return $resource('https://tcp.firebaseio.com/graph.json')

})

myApp.controller("main", function($scope, server) {

  $scope.test = "Hellow";

  $scope.data = server.query();

  $scope.data.$promise.then(function(result) {

    $scope.values = result;
    $scope.defaultValue = $scope.values[0].value;

    console.log($scope.defaultValue);

  })

  $scope.updateGraph = function(item) {

    $scope.defaultValue = item.value;

  }

});


var planVsActual = function($timeout) {

  return {

    replace: true,

    template: "<div id='pieGraph'></div>",

    link: function(scope, element, attr) {

      $timeout(function() {

        scope.$watch("defaultValue", function(newVal, oldVal) {

          var phraseValue = [newVal, 100 - newVal];
          drawPie(phraseValue);

          function drawPie(array) {

            console.log(element.width)

            var width = element.width(), height = element.height(),
                    radius = Math.min(width, height) / 1.2, data = array;

              if (!array.length) return;

              var color = d3.scale.ordinal()
                    .domain(array) 
                    .range(["#ffff00", "#1ebfc5"]);

                    var pie = d3.layout.pie()
                    .sort(null)
                    .value(function(d) { return d });

                    var arc = d3.svg.arc()
                      .outerRadius(radius - 90)
                      .innerRadius(radius - 85);

                      var svg = d3.select("#pieGraph")
                      .append("svg")
                    .attr("width", width)
                    .attr("height", height)
                    .append("g")
                    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

                    var g = svg.selectAll(".arc")
                      .data(pie(array))
                        .enter().append("g")
                        .attr("class", "arc");

                    g.append("path")
                      .attr("d", arc)
                      .style("fill", function(d,i) {  return color(d.data); });

                      g.append("text")
                     .text(array[0]+'%')
                     .attr("class", "designVal")
                     .style("text-anchor", "middle")


          }

        })

      }, 100)


    }

  }

}

angular.module("myApp")

.directive("planVsActual", planVsActual);

現場演示

您每次都會附加一個新的svg。

簡單的解決方法是先清空容器:

scope.$watch("defaultValue", function(newVal, oldVal) {

          // empty the chart element
          element.html(''); 

          // chart code    
});

我確定您可以存儲對所創建的初始對象的引用,而不是每次都創建一個新對象,這將使您可以對差異進行動畫處理,但是出於此答案的目的,我無意重寫您的d3代碼,而只是提供解決方案符合您當前的工作

工作鑽

在Angular上使用d3制作動態圖表時,最佳做法是將圖表數據綁定到指令。

<plan-vs-actual data="defaultValue"></plan-vs-actual>

在JavaScript中,您必須將scope: {data: '='}到指令定義對象。

return {
  replace: true,
  template: <div id="pieGraph></div>,
  scope: {data: '='},
  link: function(){
    //link
  }
};

此外,您希望在加載任何數據之前在鏈接函數中定義盡可能多的d3可視化效果,以實現最佳性能。 盡快加載不依賴數據的圖形部分。 在您的情況下,這意味着變量width ... height ... radius ... color.range()... pie ... arc ... svg ...都可以在范圍之外定義。 ,然后再加載任何數據。 您的鏈接如下所示:

link: function(scope,element,attr){

  $timeout(function(){

    var width = element.width(), 
          height = element.height(),
          radius = Math.min(width, height) / 1.2;

      var color = d3.scale.ordinal()
                  .range(["#ffff00", "#1ebfc5"]);

      var pie = d3.layout.pie()
                .sort(null)
                .value(function(d) { return d });

      var arc = d3.svg.arc()
                .outerRadius(radius - 90)
                .innerRadius(radius - 85);

      var svg = d3.select("#pieGraph")
          .append("svg")
          .attr("width", width)
          .attr("height", height)
          .append("g")
          .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

      //scope.$watch goes here

  },100)

}

由於您現在正在使用最佳實踐,因此scope.$watch函數現在將監視指令的data屬性,而不是defaultValue ,它看起來像這樣。

scope.$watch('data',function(newVal, oldVal){
  //code here
})

現在我們必須弄清楚scope.$watch ...中發生的最重要的變化是我們打破了.data()和.enter()方法。 下面我向您展示新的scope.$watch外觀,並帶有內聯注釋對其進行解釋。

scope.$watch("data", function(newVal, oldVal) {

      var phraseValue = [newVal, 100 - newVal];
      drawPie(phraseValue);

      function drawPie(array) {

        console.log(element.width)

                var data = array;

          if (!array.length) return;

                color.domain(array);

        //NOTICE BELOW how we select the data and then chain .enter() separately,
        //doing that allows us to use D3's enter(), exit(), update pattern

        //select all .arc from SVG and bind to data
        var g = svg.selectAll(".arc")
          .data(pie(array));

        //select all <text> from SVG and bind to data as well
        var t = svg.selectAll('text').data(array);

        //enter
        g.enter()
        .append("path")
          .attr("class", "arc")
          .attr("d", arc)
          .style("fill", function(d,i) {  return color(d.data); });

        t.enter()
        .append('text')
          .text(array[0]+"%")
          .attr("class", "designVal")
          .style("text-anchor", "middle");

        //exit
        //in this example exit() is wholly unnecessary since the # of datum
        //will never decrease. However, if it would, this would account for it.
        g.exit().remove();
        t.exit().remove();

        //update
        //update the 'd' attribute of arc and update the text of the text
        g.attr('d',arc);
        t.text(array[0]+'%');
      }

    })

參考文獻:
進入,更新,退出
D3 on Angular,作者:Ari Lerner

鏈接到分叉柱塞

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM