2

ng-repeat を使用し、AngularJS ディレクティブを使用して構築された D3 ダイヤルを使用して動的ダッシュボードを構築しています。

1 つのディレクティブ タグを実行すると、正常に動作します。ng-repeat に 2 つ以上のディレクティブ タグがある場合、ディレクティブが同じ変数を使用している奇妙な競合状態が発生しているように見えます。ディレクティブ インスタンスのスコープが完全に分離されていることを保証するにはどうすればよいですか?

プログレス変数の分離に問題があるようです。2 つのダイヤル (ディレクティブ インスタンス) が同じ進捗値を補間しており、めちゃくちゃになっています。プログレス変数をディレクティブ内の別のスコープに移動しようとしましたが、解決策が見つかりません。

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);

  });
  }
}
});
4

1 に答える 1

0

これを修正しました。主な問題は、遷移行に D3 select() を追加して、D3 が遷移中のダイヤルを認識できるようにすることだと思います。また、追加の SVG をディレクティブから引き出して、ドキュメントに SVG を 1 つだけ含めるようにしました。ディレクティブは、SVG タグの下にグループを追加しています。すごいね!

HTML:

<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);
                };
            });



  });
}
}
});
于 2013-09-10T06:24:14.400 に答える