1

Trying to make this Apache ECharts Draggable Example work in Angular.

I've converted the event listener and draggable functions over to Angular like this (Stackblitz Link):

import { Component } from '@angular/core';
import { fromEvent, debounceTime } from 'rxjs';
import { EChartsOption, graphic } from 'echarts';
import * as echarts from 'echarts';

let symbolSize = 20;
let data = [
  [15, 0],
  [-50, 10],
  [-56.5, 20],
  [-46.5, 30],
  [-22.1, 40],
];

let options: EChartsOption = {
  title: {
    text: 'Try Dragging these Points',
    left: 'center',
  },
  tooltip: {
    triggerOn: 'none',
    formatter: function (params) {
      return (
        'X: ' +
        params.data[0].toFixed(2) +
        '<br>Y: ' +
        params.data[1].toFixed(2)
      );
    },
  },
  grid: {
    top: '8%',
    bottom: '12%',
  },
  xAxis: {
    min: -100,
    max: 80,
    type: 'value',
    axisLine: { onZero: false },
  },
  yAxis: {
    min: -30,
    max: 60,
    type: 'value',
    axisLine: { onZero: false },
  },
  series: [
    {
      id: 'a',
      type: 'line',
      smooth: true,
      symbolSize: symbolSize,
      data: data,
    },
  ],
};

@Component({
  selector: 'chart',
  templateUrl: `./chart.component.html`,
  styles: [`h1 { font-family: Lato; }`],
})
export class ChartComponent {
  echartsIntance: any;
  options: EChartsOption;
  constructor() {
    this.resize();
  }
  ngOnInit(): void {
    this.options = options;
  }

  showTooltip(dataIndex: number) {
    this.echartsIntance.dispatchAction({
      type: 'showTip',
      seriesIndex: 0,
      dataIndex: dataIndex,
    });
  }
  hideTooltip(dataIndex: number) {
    this.echartsIntance.dispatchAction({
      type: 'hideTip',
    });
  }

  resize() {
    fromEvent(window, 'resize')
      .pipe(debounceTime(200))
      .subscribe((e) => {
        console.log('RESIZE');
        if (this.echartsIntance) {
          this.echartsIntance.resize({
            animation: {
              duration: 1500,
              easing: 'elasticOut',
            },
          });
        }
      });
  }

  onChartInit(echarts) {
    this.echartsIntance = echarts;
    this.makeDraggable(echarts, this.onPointDragging);
    this.addEventListeners(
      echarts,
      this.showTooltip,
      this.hideTooltip,
      this.onPointDragging
    );
  }

  addEventListeners(
    echartInstance: any,
    showTooltip: any,
    hideTooltip: any,
    onPointDragging: any
  ) {
    echartInstance.setOption({
      graphic: data.map(function (item, dataIndex) {
        return {
          type: 'circle',
          position: echartInstance.convertToPixel('grid', item),
          shape: {
            cx: 0,
            cy: 0,
            r: symbolSize / 2,
          },
          invisible: true,
          draggable: true,
          ondrag: function (dx: number, dy: number) {
            onPointDragging(dataIndex, [(this as any).x, (this as any).y]);
          },
          onmousemove: function () {
            showTooltip(dataIndex);
          },
          onmouseout: function () {
            hideTooltip(dataIndex);
          },
          z: 100,
        };
      }),
    });
  }

  makeDraggable(echartsIntance: any, onPointDragging: any) {
    echartsIntance.setOption({
      // Declare a graphic component, which contains some graphic elements
      // with the type of 'circle'.
      // Here we have used the method `echarts.util.map`, which has the same
      // behavior as Array.prototype.map, and is compatible with ES5-.
      graphic: echarts.util.map(data, function (dataItem, dataIndex) {
        return {
          // 'circle' means this graphic element is a shape of circle.
          type: 'circle',

          shape: {
            // The radius of the circle.
            r: symbolSize / 2,
          },
          // Transform is used to located the circle. position:
          // [x, y] means translate the circle to the position [x, y].
          // The API `convertToPixel` is used to get the position of
          // the circle, which will introduced later.
          position: echartsIntance.convertToPixel('grid', dataItem),

          // Make the circle invisible (but mouse event works as normal).
          invisible: true,
          // Make the circle draggable.
          draggable: true,
          // Give a big z value, which makes the circle cover the symbol
          // in line series.
          z: 100,
          // This is the event handler of dragging, which will be triggered
          // repeatly while dragging. See more details below.
          // A util method `echarts.util.curry` is used here to generate a
          // new function the same as `onPointDragging`, except that the
          // first parameter is fixed to be the `dataIndex` here.
          ondrag: echarts.util.curry(onPointDragging, dataIndex),
        };
      }),
    });
  }

  onPointDragging(dataIndex: number, pos: number[]) {
    data[dataIndex] = this.echartsIntance.convertFromPixel('grid', pos);

    // Update data
    this.echartsIntance.setOption({
      series: [
        {
          id: 'a',
          data: data,
        },
      ],
    });
  }
}


But the points are not dragging. Any ideas?

Ole
  • 41,793
  • 59
  • 191
  • 359
  • I found two minor issues: 1) when `onChartInit` is called the chart is not ready to convert to pixels; you have to let the current sequence of code to complete with a `setTimeout(,0)` (this also happens in the original example); 2) the value of `this` is lost in the functions inside the argument to `echartInstance.setOption({...})`. You may track [the changes](https://stackblitz.com/edit/angular-ivy-1ashgy?file=src%2Fapp%2Fchart.component.ts) I made by looking for `_this`. – kikon Jul 29 '23 at 13:28
  • 1
    Brilliant - Thank you!! – Ole Jul 30 '23 at 04:04

0 Answers0