【问题标题】:angularJS convert d3js custom donut directive to componentangularJS 将 d3js 自定义甜甜圈指令转换为组件
【发布时间】:2016-12-30 07:52:41
【问题描述】:

所以我正在尝试将指令转换为组件。当给定输入数据集时,该指令基本上会呈现带有图例的圆环图。此外,当您将鼠标悬停在甜甜圈上的弧上时,我会显示弹出动画。该指令工作得很好。问题是当我将指令转换为组件时。请在下面找到组件的代码

(() => {
  angular
  .module('charts.donut-chart')
  .component('donutChart', {
    templateUrl: 'charts/donut-chart/donut-chart.html',
    controller: DonutChartController,
    bindings: {
      chartData: '=',
      chartColors: '=',
      chartHeight: '=',
      chartWidth: '=',
      legendHeight: '=',
      hover: '@',
      tooltips: '=',
      enableLegend: '=',
      id: '=',
    },
  });

  function DonutChartController ($document, donutOptionsFactory, $filter, $scope, $timeout, $window, $element, $attrs, $compile) {
    // console.log($element, $attrs);

    const d3 = $window.d3;

    const vm = this;

    const timestamp = new Date().getTime();

    vm.chartId = `donut_chart_${timestamp}`;

    const donutOptions = {
      chartWidth: vm.chartWidth,
      chartHeight: vm.chartHeight,
      legendHeight: vm.legendHeight,
    };

    // chart options
    let chartWidth;
    let chartHeight;
    let enableLegend;
    let legendHeight;
    let outerRadiusOfArc;
    let innerRadiusOfArc;
    let color;
    let arcColors;

    let pie;
    let arc;

    let svgContainer;
    let formattedDonutChartOptions;
    let svgElement;

    const deregistrationFn = $scope.$watch(() => $document[0].querySelector(`#${this.chartId}`), (newValue) => {
      if (newValue !== null) {
        deregistrationFn();
        svgContainer = d3.select(`#${vm.chartId}`);
        vm.initChartOptions();
        createChart();
        // bindMouseEvents();
      }
    });

    vm.initChartOptions = () => {
      formattedDonutChartOptions = donutOptionsFactory.getOptionsForDonutChart(donutOptions, svgContainer);
      chartWidth = formattedDonutChartOptions.chartWidth;
      chartHeight = formattedDonutChartOptions.chartHeight;
      enableLegend = formattedDonutChartOptions.enableLegend;
      legendHeight = formattedDonutChartOptions.legendHeight;
      outerRadiusOfArc = formattedDonutChartOptions.outerRadiusOfArc;
      innerRadiusOfArc = formattedDonutChartOptions.innerRadiusOfArc;
      color = formattedDonutChartOptions.chartColors;
    };

    function onArcMouseOver (d, path) {
      console.log('mouseover', d, path);
      d3.select(path).transition()
        .attr('d', d3.svg.arc()
          .innerRadius(outerRadiusOfArc * 1.5)
          .outerRadius(outerRadiusOfArc));
    }

    function onArcMouseOut (d, path) {
      console.log('mouseout', d, path);
      d3.select(path).transition()
          .duration(500)
          .ease('bounce')
          .attr('d', d3.svg.arc()
            .innerRadius(innerRadiusOfArc)
            .outerRadius(outerRadiusOfArc));
    }

    function createChart () {
      arcColors = d3.scale.ordinal()
        .range(color);

      pie = d3.layout.pie()
        .sort(null)
        .value(d => d.value);

      arc = d3.svg.arc()
        .innerRadius(innerRadiusOfArc)
        .outerRadius(outerRadiusOfArc);

      svgElement = svgContainer.append('svg')
        .attr('width', chartWidth)
        .attr('height', chartHeight)
        .append('g')
        .attr('transform', `translate(${chartWidth / 2}, ${chartHeight / 2})`);

      svgElement.selectAll('path')
        .data(pie(vm.chartData))
        .enter()
        .append('path')
        .attr('fill', (d, i) => arcColors(i))
        .attr('d', arc)
        .on('mouseover', (d, i, j) => {
          console.log(d, i, j, this);
          const ref = this;
          const dObject = d;
          // (() => {
          // onArcMouseOver(dObject, d3.select(this));
          // })(dObject, ref);
          d3.select(this).transition()
            .attr('d', d3.svg.arc()
              .innerRadius(outerRadiusOfArc * 1.5)
              .outerRadius(outerRadiusOfArc));
        })
        .on('mouseout', (d, i, j) => {
          console.log(d, i, j, this);
          // onArcMouseOut(d, d3.select(this));
          const ref = this;
          const dObject = d;
          // (() => {
          // onArcMouseOut(dObject, d3.select(this));
          // })(dObject, ref);
          d3.select(this).transition()
              .duration(500)
              .ease('bounce')
              .attr('d', d3.svg.arc()
                .innerRadius(innerRadiusOfArc)
                .outerRadius(outerRadiusOfArc));
        });
    }


    function bindMouseEvents () {
      /* function pathAnim (path, dir) {
        switch (dir) {
        case 0: // mouseout
          path.transition()
              .duration(500)
              .ease('bounce')
              .attr('d', d3.svg.arc()
                .innerRadius(innerRadiusOfArc)
                .outerRadius(outerRadiusOfArc));
          break;
        case 1:// mouseover
          path.transition()
            .attr('d', d3.svg.arc()
              .innerRadius(outerRadiusOfArc * 1.5)
              .outerRadius(outerRadiusOfArc));
          break;
        default: break;
        }
      }*/

      const eventObject = {
        mouseover (d) {
          console.log('mouseover', this, d);
          // pathAnim(d3.select(this), 1);
        },
        mouseout (d) {
          console.log('mouseout', this, d);
          // pathAnim(d3.select(this), 0);
        },
      };
      svgElement.on(eventObject);
    }
  }
})();

上面的模板是

<div layout="row" id={{$ctrl.chartId}} layout-align="center center"></div>

上述组件工作正常并按预期呈现圆环图。问题区域是我无法像指令那样做悬停效果。我尝试了两种绑定鼠标事件的方法。第一个是单独使用函数 bindMouseEvents(),该函数工作并返回“d”参数以及弧的所有起始角和结束角值,但这些函数中的 this 未定义或指向 DonutController。 this 应该指向它没有的悬停元素。

所以我尝试了第二种方法。我将添加数据的事件绑定到路径部分.on('mouseover', (d, i, j) =&gt; {。我分配匿名回调,并在其中触发我自己的函数,将参数传递给我自己的函数。在这里,当我使用 chrome 的调试器调试代码时,它显示this 正确指向悬停的元素,但是当我将它传递给我自己的函数时,this 的所有对象值都被传递为空或未定义,并且因此我的动画失败了。

因此,在第一种方法中,我根据需要获得了 d 对象,但 this 被搞砸了,在第二种方法中,我正确地获得了 this,但是当我调用我的函数并将 this 传递给它时,它被作为一个空对象(具有所有键但这些键的值是空或未定义的对象)传递。

有人能指出我在搞砸什么吗?或者有什么更好的方法将我的指令转换为组件?

提前致谢

【问题讨论】:

    标签: javascript angularjs d3.js angularjs-components


    【解决方案1】:

    使用粗箭头表示法的主要原因之一是保留父作用域中的this。所以当你这样称呼它时:

    .on('mouseover', (d, i, j) => {
    

    this 被保留,d3 不适用于注入悬停元素。简单的解决方法是使用常规功能:

    .on('mouseover', function(d, i, j){
    

    这是您的代码的工作演示:

    (() => {
      angular
        .module('charts.donut-chart')
        .component('donutChart', {
          template: "<div layout=\"ro\" id={{$ctrl.chartId}} layout-align=\"center center\"></div>",
          controller: DonutChartController,
          bindings: {
            chartData: '=',
            chartColors: '=',
            chartHeight: '=',
            chartWidth: '=',
            legendHeight: '=',
            hover: '@',
            tooltips: '=',
            enableLegend: '=',
            id: '=',
          },
        });
    
      function DonutChartController($document, $filter, $scope, $timeout, $window, $element, $attrs, $compile) {
        // console.log($element, $attrs);
    
        const d3 = $window.d3;
    
        const vm = this;
    
        const timestamp = new Date().getTime();
    
        vm.chartId = `donut_chart_${timestamp}`;
    
        const donutOptions = {
          chartWidth: vm.chartWidth,
          chartHeight: vm.chartHeight,
          legendHeight: vm.legendHeight,
        };
    
        // chart options
        let chartWidth;
        let chartHeight;
        let enableLegend;
        let legendHeight;
        let outerRadiusOfArc;
        let innerRadiusOfArc;
        let color;
        let arcColors;
    
        let pie;
        let arc;
    
        let svgContainer;
        let formattedDonutChartOptions;
        let svgElement;
    
        const deregistrationFn = $scope.$watch(() => $document[0].querySelector(`#${this.chartId}`), (newValue) => {
          if (newValue !== null) {
            deregistrationFn();
            svgContainer = d3.select(`#${vm.chartId}`);
            vm.initChartOptions();
            createChart();
          }
        });
    
        vm.initChartOptions = () => {
          //formattedDonutChartOptions = donutOptionsFactory.getOptionsForDonutChart(donutOptions, svgContainer);
          chartWidth = 500; //formattedDonutChartOptions.chartWidth;
          chartHeight = 500; //formattedDonutChartOptions.chartHeight;
          enableLegend = true; //formattedDonutChartOptions.enableLegend;
          legendHeight = 50; //formattedDonutChartOptions.legendHeight;
          outerRadiusOfArc = 250; //formattedDonutChartOptions.outerRadiusOfArc;
          innerRadiusOfArc = 200; //formattedDonutChartOptions.innerRadiusOfArc;
          color = ['red', 'green', 'yellow']; //formattedDonutChartOptions.chartColors;
        };
    
        function onArcMouseOver(d, path) {
          console.log('mouseover', d, path);
          d3.select(path).transition()
            .attr('d', d3.svg.arc()
              .innerRadius(outerRadiusOfArc * 1.5)
              .outerRadius(outerRadiusOfArc));
        }
    
        function onArcMouseOut(d, path) {
          console.log('mouseout', d, path);
          d3.select(path).transition()
            .duration(500)
            .ease('bounce')
            .attr('d', d3.svg.arc()
              .innerRadius(innerRadiusOfArc)
              .outerRadius(outerRadiusOfArc));
        }
    
        function createChart() {
          arcColors = d3.scale.ordinal()
            .range(color);
    
          pie = d3.layout.pie()
            .sort(null)
            .value(d => d.value);
    
          arc = d3.svg.arc()
            .innerRadius(innerRadiusOfArc)
            .outerRadius(outerRadiusOfArc);
    
          svgElement = svgContainer.append('svg')
            .attr('width', chartWidth)
            .attr('height', chartHeight)
            .append('g')
            .attr('transform', `translate(${chartWidth / 2}, ${chartHeight / 2})`);
    
          svgElement.selectAll('path')
            .data(pie([{
              value: 10
            }, {
              value: 20
            }, {
              value: 30
            }]))
            .enter()
            .append('path')
            .attr('fill', (d, i) => arcColors(i))
            .attr('d', arc)
            .on('mouseover', function(d, i, j) {
              d3.select(this).transition()
                .attr('d', d3.svg.arc()
                  .innerRadius(outerRadiusOfArc * 1.5)
                  .outerRadius(outerRadiusOfArc));
            })
            .on('mouseout', function(d, i, j) {
              d3.select(this).transition()
                .duration(500)
                .ease('bounce')
                .attr('d', d3.svg.arc()
                  .innerRadius(innerRadiusOfArc)
                  .outerRadius(outerRadiusOfArc));
            });
        }
      }
    })();
    <!doctype html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
    
      <script src="//code.angularjs.org/snapshot/angular.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
      <script src="index.js"></script>
      <script src="donutChart.js"></script>
    
    
    </head>
    
    <body ng-app="charts.donut-chart">
      <!-- components match only elements -->
      <div ng-controller="MainCtrl as ctrl">
        <donut-chart></donut-detail>
      </div>
    
      <script>
        (function(angular) {
          'use strict';
          angular.module('charts.donut-chart', []).controller('MainCtrl', function MainCtrl() {
    
          });
        })(window.angular);
      </script>
    
    </body>
    
    </html>

    【讨论】:

    • 感谢@mark 的回复。我发现使用箭头符号不会保留this。 Mozilla 的文档帮助了我。
    猜你喜欢
    • 1970-01-01
    • 2015-03-07
    • 2011-05-15
    • 2014-05-17
    • 1970-01-01
    • 1970-01-01
    • 2022-09-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多