如何将`d3js`图与angular指令集成

时间:2015-07-24 14:30:05

标签: angularjs d3.js

我正在尝试将$ git add assets/* The following paths are ignored by one of your .gitignore files: assets/avatars Use -f if you really want to add them. fatal: no files added 图表与我的角度指令集成。但我没有得到预期的结果。

  1. 图表未正确附加到div;相反,我得到d3js标签
  2. 每次点击列表(' undefined%),而不是更新图表,它会附加新图表。
  3. 请点击顶部的列表。

    这是我的代码和演示:

    li

    Live Demo

2 个答案:

答案 0 :(得分:1)

您每次都会附加一个新的svg。

简单的解决方法是首先清空容器:

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

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

          // chart code    
});

我确信您可以存储对创建的初始对象的引用,而不是每次都创建一个新对象,这样可以让您为差异设置动画但是为了这个答案的目的,我不打算重写你的d3代码而且我是简单地提供适合您当前所做工作的解决方案

working plunker

答案 1 :(得分:1)

在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 ...都可以在范围之外定义。$ watch function ,在加载任何数据之前。您的链接将如下所示:

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]+'%');
      }

    })

参考文献:
Enter, Update, Exit
D3 on Angular by Ari Lerner

Link to Forked Plunker