简体   繁体   English

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

[英]angularJS convert d3js custom donut directive to component

So I am trying to convert a directive to component. 所以我试图将指令转换为组件。 The directive basically renders a donut chart with legends when given an input dataset. 给定输入数据集时,该指令基本上会绘制带有图例的甜甜圈图。 Also when you hover on arc on donut I show popped up animation. 另外,当您将鼠标悬停在圆环上时,我会显示弹出的动画。 The directive works absolutely fine. 该指令工作得很好。 The problem is when I converted the directive to component. 问题是当我将指令转换为组件时。 Please find the code below for component 请在下面找到组件的代码

(() => {
  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);
    }
  }
})();

The template for above is 上面的模板是

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

The above component works fine and renders the donut graph as intended. 上面的组件工作正常,并按预期呈现了甜甜圈图。 The problem area is I am not able to do hover effects like the directive. 问题所在是我无法像指令一样执行悬停效果。 I have tried two ways to bind mouse events. 我尝试了两种方法来绑定鼠标事件。 The first one is in separate using function bindMouseEvents() this function works and returns me the 'd' argument with all the startangle and endangle values for the arc but the this in these function is undefined or points to DonutController. 第一个是使用功能bindMouseEvents()单独使用的,此功能可以正常工作,并向我返回“ d”参数以及圆弧的所有起始角和终止角值,但这些函数中的this未定义或指向DonutController。 The this should point to the hovered element which it does not. this应该指向它没有的悬停元素。

So I tried second approach. 所以我尝试了第二种方法。 I bind the events where I am appending the data to path section .on('mouseover', (d, i, j) => { . I assign anonymous call backs and from within it i trigger my own function passing the arguments to my own function. Here when I am debugging the code using chrome's debugger it shows me that this is pointing correctly to the hovered element but when I am passing it to my own function all the object values of this are being passed as empty or undefined and hence my animation fails. 我将将数据追加到路径部分的事件绑定到.on('mouseover', (d, i, j) => { 。我分配匿名.on('mouseover', (d, i, j) => {并从其中触发我自己的函数,将参数传递给我自己的功能。在这里,当我使用Chrome的调试它为我调试的代码, this是正确指向悬停元素,但是当我把它传递给我的函数的所有对象值this被传递为空或不确定的,因此我的动画失败了。

So in the first approach I get the d object as necessary but the this is messed up and in the second approach i get the this properly but when I call my function and pass this to it, it is being passed as an empty object (object that has all keys but the value of those keys is either empty or undefined). 所以在第一种方法我得到的d对象必要的,但在this被搞砸了,并在第二个方法我得到的this正确的,但是当我把我的功能,并通过this它,它被传递作为一个空对象(物体具有所有键,但这些键的值为空或未定义)。

Can some one point out what I am messing up? 有人可以指出我在搞什么吗? Or any better way to convert my directive to component? 还是将指令转换为组件的更好方法?

Thanks in advance 提前致谢

One of the primary reasons for fat-arrow notation is to preserve the this from the parent scope. 其中一个主要的原因脂肪箭头符号是保留this从父范围。 So when you call it like: 所以当你这样称呼它时:

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

this is preserved and d3 is not apply to inject the hovered element. this将保留,并且d3不适用于注入悬停的元素。 Simple fix is to use a conventional function: 简单的解决方法是使用常规功能:

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

Here's a working demonstration with your code: 这是您的代码的有效演示:

 (() => { 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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM