在ng repeat中隔离AngularJS指令中多个d3组件的作用域

在ng repeat中隔离AngularJS指令中多个d3组件的作用域,angularjs,d3.js,angularjs-directive,Angularjs,D3.js,Angularjs Directive,我正在使用ng repeat和使用AngularJS指令构建的D3拨号盘构建一个动态仪表板 当我运行1指令标记时,它工作正常。当我在ng repeat中有2+个指令标记时,它看起来像是一个奇怪的竞争条件,其中指令使用相同的变量。如何保证指令实例具有完全隔离的作用域 看起来我在隔离进度变量时遇到了问题。2个刻度盘(指令实例)插入了相同的进度值,结果搞砸了。我尝试将progress变量移动到指令中的不同范围,但找不到解决方案 HTML: 我把这个修好了。我认为主要的问题是在转换线上添加一个D3 se

我正在使用ng repeat和使用AngularJS指令构建的D3拨号盘构建一个动态仪表板

当我运行1指令标记时,它工作正常。当我在ng repeat中有2+个指令标记时,它看起来像是一个奇怪的竞争条件,其中指令使用相同的变量。如何保证指令实例具有完全隔离的作用域

看起来我在隔离进度变量时遇到了问题。2个刻度盘(指令实例)插入了相同的进度值,结果搞砸了。我尝试将progress变量移动到指令中的不同范围,但找不到解决方案

HTML:


我把这个修好了。我认为主要的问题是在转换线上添加一个D3 select(),以便D3知道它正在转换哪个拨号盘。我还从指令中删除了append SVG,现在文档中只有一个SVG。该指令正在SVG标记下追加组。看起来棒极了

HTML:

 <div ng-controller="DashboardCtrl" ng-init="init();">
<div ng-repeat="item in metrics">
  <div ng-switch on="item.type">
    <div ng-switch-when="dial">
        <gh-dial val="item.data" data-format="item.data-format" metric-title="item.title" gh-target="item.target"></gh-dial>
    </div>
    <div ng-switch-when="meter">
        <gh-meter val="item.data" data-format="item.data-format" metric-title="item.title" gh-target="item.target"></gh-meter>
    </div>
  </div>
</div>
 </div>
directives.directive('ghDial', function () {

var width = 370,
    height = 370,
    twoPi = 2 * Math.PI,
    progress = 0;

return {
  restrict: 'E',
  scope: {
    val: '=',
  dataFormat: '=',
  metricTitle: '=',
  ghTarget: '='
},
link: function (scope, element, attrs) {

  console.debug(scope.dataFormat);
   var formatPercent = d3.format(scope.dataFormat);
   var total = scope.ghTarget.valueOf() ;
   var prepend = "" ;
   if (scope.dataFormat === "$") scope.prepend = "$" ;
   console.debug("prepend: "+scope.prepend);
   console.debug("data format: "+scope.dataFormat );

  // set up initial svg object
var vis = d3.select(element[0]).append("svg")
    .attr("width", width)
    .attr("height", height)
    .attr('fill', '#2E7AF9')
    .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");


  scope.$watch('val', function (newVal, oldVal) {


      vis.selectAll('*').remove();


        // if 'val' is undefined, exit
        if (!newVal) {
          return;
        }

        var arc = d3.svg.arc()
            .startAngle(0)
            .innerRadius(140)
            .outerRadius(170)
        ;

        var meter = vis.append("g")
            .attr("class", "progress-meter");

        meter.append("path")
            .attr("class", "background")
            .attr("d", arc.endAngle(twoPi));

        var foreground = meter.append("path")
            .attr("class", "foreground");

        var text = meter.append("text")
            .attr("text-anchor", "middle")
            .style("font-size","14px");

        var text2 = meter.append("text")
            .attr("y", 40)
            .attr("text-anchor", "middle")
            .attr("class", "text2");

            console.debug(scope.metricTitle);
            text2.text(scope.metricTitle);

        var animate = function(percentage) {

            var i = d3.interpolate(progress, percentage/total);

            d3.transition().duration(800).tween("progress", function () {
                return function (t) {
                    progress = i(t);
                    foreground.attr("d", arc.endAngle(twoPi * progress));
                    console.debug("progress:"+progress);
                    text.text(prepend+''+percentage);
                };
            });
        }; 

        setTimeout(function () {
        console.debug(newVal);
        animate(newVal.expr0.valueOf());
        }, 500);

  });
  }
}
});
<div ng-controller="DashboardCtrl" ng-init="init();">
<svg id="d3Parent" width="1000" height="1000" fill="#2E7AF9">
</svg>
<div ng-repeat="item in metrics">
  <div ng-switch on="item.type">
    <div ng-switch-when="dial">
        <gh-dial val="item.data" data-format="item.data-format" metric-title="item.title" gh-target="item.target"></gh-dial>
    </div>
    <div ng-switch-when="meter">
        <gh-meter val="item.data" data-format="item.data-format" metric-title="item.title" gh-target="item.target"></gh-meter>
    </div>
  </div>
</div>
</div>
directives.directive('ghDial', function () {

var width = 370,
    height = 370,
    twoPi = 2 * Math.PI,
    progress = 0.00001;

return {
  restrict: 'E',
transclude: true,
  scope: {
      val: '=',
  dataFormat: '=',
  metricTitle: '=',
  ghTarget: '='
  },
  link: function (scope, element, attrs) {

  console.debug(attrs['data-format']);
   var formatPercent = d3.format(scope.dataFormat);
   var total = scope.ghTarget.valueOf() ;
   var prepend = "" ;
   if (scope.dataFormat === "$") prepend = "$" ;
   console.debug("prepend: "+scope.prepend);
   console.debug("data format: "+scope.dataFormat );

   var index = document.querySelector("#d3Parent").childNodes.length-1 ;

   var column = [200, 600];
   var row = [200, 500, 750]

  // set up initial svg object
var vis = d3.select("#d3Parent")
        .append("g")
        .attr("transform", "translate(" + column[index%2] + "," + row[0] + ")");  

  scope.$watch('val', function (newVal, oldVal) {

      console.debug("directive watch fired:"+scope.metricTitle);

      vis.selectAll('*').remove();

        // if 'val' is undefined, exit
        if (!newVal) {
          return;
        }

        var arc = d3.svg.arc()
            .startAngle(0)
            .innerRadius(140)
            .outerRadius(170)
        ;

        var meter = vis.append("g")
            .attr("class", "progress-meter");

        meter.append("path")
            .attr("class", "background")
            .attr("d", arc.endAngle(twoPi));

        var foreground = meter.append("path")
            .attr("class", "foreground");

        var text = meter.append("text")
            .attr("text-anchor", "middle")
            .style("font-size","24px");

        var text2 = meter.append("text")
            .attr("y", 40)
            .attr("text-anchor", "middle")
            .attr("class", "text2");

            console.debug(scope.metricTitle);
            text2.text(scope.metricTitle);

        var percentage = newVal.expr0.valueOf() ;


            console.debug("animate progress: "+progress+" percentage:"+percentage+" total:"+total);
            var i = d3.interpolateNumber(progress, percentage/total);

            d3.select(vis).transition().duration(800).tween("progress", function () {
                return function (t) {
                    progress = i(t);
                    foreground.attr("d", arc.endAngle(twoPi * progress));
                    console.debug("progress:"+progress);
                    text.text(prepend+''+percentage);
                };
            });



  });
}
}
});