angularJS将d3js自定义甜甜圈指令转换为组件

时间:2016-12-30 07:52:41

标签: javascript angularjs d3.js angularjs-components

所以我试图将指令转换为组件。在给定输入数据集时,该指令基本上呈现带有图例的圆环图。此外,当你在圆环上盘旋时,我会显示弹出的动画。该指令绝对正常。问题是当我将指令转换为组件时。请在下面找到组件

的代码
(() => {
  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()这个函数工作并返回我的&#39; d&#39;带有弧的所有startangle和endangle值的参数,但这些函数中的this未定义或指向DonutController。 this应指向悬而未决的元素。

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

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

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

提前致谢

1 个答案:

答案 0 :(得分:0)

fat-arrow表示法的主要原因之一是保留父作用域中的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>