angular中sortable.js和echarts的冲突

我遇到的问题是:使用sortable.js插件进行拖拽列表,列表里使用了echarts插件,多次拖拽同一个echarts会导致echarts内容丢失
使用的技术栈:angular12、sortableJs、echarts

这是我echarts的ts

import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { DemandService } from '../demand.service';
import * as echarts from 'echarts';
@Component({
 // eslint-disable-next-line @angular-eslint/component-selector
 selector: 'ori-echarts',
 templateUrl: './echarts.component.html',
 styleUrls: ['./echarts.component.less']
})
export class EchartsComponent implements OnInit, AfterViewInit {
 constructor(private ds: DemandService) { }
 @Input() options;
 @ViewChild('chartContainer') chartContainer!: ElementRef;
 chartInstance
 ngOnInit(): void {}
 ngAfterViewInit(): void {
   this.initEcahrts();

 }
 echartsInstanceIds = [];
 initEcahrts() {
   this.updateEcharts('init');
 }
 refreshEcharts() {
   this.updateEcharts('refresh');
 }

 updateEcharts(optionType: 'init'|'refresh') {
   const chartElement = document.createElement('div');
   chartElement.style.width = '100%';
   chartElement.style.height = '100%';
   chartElement.id = 'ec_' + new Date().getTime();
   if(optionType === 'refresh') {
     if (this.chartContainer.nativeElement.querySelector('div')) {
       this.chartContainer.nativeElement.querySelector('div').remove();
     }
   }
   this.chartContainer.nativeElement.appendChild(chartElement);
   this.chartInstance = echarts.init(chartElement);
   this.chartInstance.setOption(this.options);
   this.chartInstance.resize();
 }
}

这是我echarts的html

<div #chartContainer style="width: 100%; height: 100%;"></div>

我在拖拽文件里写了如下代码片段


addProxyBindingCopyDrag() {
    this.ds.proxySortableListDom = document.getElementById('proxy-sort-list');
    const that = this;
    const sortable = Sortable.create(this.ds.proxySortableListDom, {
      group: {
        name: 'proxyDrag',
        pull: 'clone',
        put: false
      },
      removeCloneOnHide: false,
      filter: '.has-dragged-item',
      animation: 150,
      sort: false,
      onStart: (evt) => {
        const index = evt.clone.getAttribute('index');
        this.dppChartComponent.refreshEcharts();
      },
      onEnd: (evt) => {
        const index = evt.clone.getAttribute('index');
        if (!that.ds.proxyList[index].dragged) {
          that.ds.proxyList[index].dragged = true;
        }
        this.dppChartComponent.refreshEcharts();
      }
    });
  }

结果第一次拖拽是好的

img

但到了第二次拖拽同一个图的时候

img

我本来以为是echarts的实例id相同的问题,但是现在的实现效果,每一次拖拽过来的echarts图实现了_echarts_instance_不同了,echarts还是会丢失

img

现在我不太清楚问题出在哪里了,请各位指点迷津!

【以下回答由 GPT 生成】

分析

根据提供的代码,我们可以看到问题出现在拖拽同一个echarts图表时丢失内容。主要原因是每次拖拽都在重新创建一个echarts实例,但是并没有销毁之前的实例,导致页面上存在多个实例,并且重复调用resize方法可能会出现问题。

解决方案

解决这个问题的关键在于正确创建和销毁echarts实例,并且避免重复调用resize方法。

修改EchartsComponent组件

首先,我们需要在EchartsComponent组件中添加一个销毁echarts实例的方法,并在组件销毁的时候调用该方法。

import { OnDestroy } from '@angular/core';
// ...

export class EchartsComponent implements OnInit, AfterViewInit, OnDestroy {

  // ...

  ngOnDestroy(): void {
    this.destroyEcharts();
  }

  destroyEcharts() {
    if (this.chartInstance) {
      this.chartInstance.dispose();
      this.chartInstance = null;
    }
  }
}

修改拖拽文件中的代码

在拖拽文件的代码中,我们需要修改refreshEcharts方法,当echarts实例存在时才调用resize方法。

refreshEcharts() {
  if (this.dppChartComponent.chartInstance) {
    this.dppChartComponent.chartInstance.resize();
  }
}

修改initEcahrts方法

原先的initEcahrts方法在每次重新渲染组件时都会调用,导致创建多个echarts实例。我们需要在创建echarts实例前判断是否已经存在实例,如果存在则直接调用setOption方法更新数据。

initEcahrts() {
  if (!this.chartInstance) {
    this.createEcharts();
  } else {
    this.chartInstance.setOption(this.options);
    this.chartInstance.resize();
  }
}

createEcharts() {
  const chartElement = document.createElement('div');
  chartElement.style.width = '100%';
  chartElement.style.height = '100%';
  chartElement.id = 'ec_' + new Date().getTime();

  if (this.chartContainer.nativeElement.querySelector('div')) {
    this.chartContainer.nativeElement.querySelector('div').remove();
  }

  this.chartContainer.nativeElement.appendChild(chartElement);
  this.chartInstance = echarts.init(chartElement);
  this.chartInstance.setOption(this.options);
  this.chartInstance.resize();
}

完整代码

import { AfterViewInit, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { DemandService } from '../demand.service';
import * as echarts from 'echarts';

@Component({
  selector: 'ori-echarts',
  templateUrl: './echarts.component.html',
  styleUrls: ['./echarts.component.less']
})
export class EchartsComponent implements OnInit, AfterViewInit, OnDestroy {

  constructor(private ds: DemandService) { }

  @Input() options;
  @ViewChild('chartContainer') chartContainer!: ElementRef;
  chartInstance;

  ngOnInit(): void {
  }

  ngAfterViewInit(): void {
    this.initEcahrts();
  }

  ngOnDestroy(): void {
    this.destroyEcharts();
  }

  echartsInstanceIds = [];

  initEcahrts() {
    if (!this.chartInstance) {
      this.createEcharts();
    } else {
      this.chartInstance.setOption(this.options);
      this.chartInstance.resize();
    }
  }

  createEcharts() {
    const chartElement = document.createElement('div');
    chartElement.style.width = '100%';
    chartElement.style.height = '100%';
    chartElement.id = 'ec_' + new Date().getTime();

    if (this.chartContainer.nativeElement.querySelector('div')) {
      this.chartContainer.nativeElement.querySelector('div').remove();
    }

    this.chartContainer.nativeElement.appendChild(chartElement);
    this.chartInstance = echarts.init(chartElement);
    this.chartInstance.setOption(this.options);
    this.chartInstance.resize();
  }

  destroyEcharts() {
    if (this.chartInstance) {
      this.chartInstance.dispose();
      this.chartInstance = null;
    }
  }

  refreshEcharts() {
    if (this.chartInstance) {
      this.chartInstance.resize();
    }
  }
}

总结

通过以上修改,我们解决了在拖拽同一个echarts图表时导致内容丢失的问题。主要是通过正确创建和销毁echarts实例,并避免重复调用resize方法来解决冲突。希望对你有所帮助!


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^