Angular 组件 ngDestroy 没有被调用,图表也没有被销毁

问题描述 投票:0回答:1

我正在将现有的 Chart JS 2 代码迁移到 3.9.1 并将 Angular 14 升级到 16。这是我的代码:

图表线.component.html

<!--chart container -->
<div id="container">
  <div id="scrollArea" class="CMI-ChartWrapper">
    <div class="CMI-Chart">
      <canvas baseChart #lineCanvas [id]="idChartname"></canvas>
    </div>
  </div>
  <canvas #targetCanvas id="chartAxis" height="400"></canvas>
</div>

图表线.component.ts

 import { Component, OnInit, ViewChild, ElementRef, Input, OnDestroy } from "@angular/core";
    import Chart from "chart.js/auto";
    import ChartDataLabels from 'chartjs-plugin-datalabels';
    import { DataServiceService } from 'src/app/services/data-service/data-service.service';
    import { data } from '../data';
    
    @Component({
      selector: 'app-chart-line',
      templateUrl: './chart-line.component.html',
      styleUrls: ['./chart-line.component.scss'],
    })
    export class ChartLineComponent implements OnInit, OnDestroy {
      @ViewChild('scroll') scroll: any;
      @ViewChild('targetCanvas', { static: true }) targetCanvasRef: ElementRef<HTMLCanvasElement>;
      @ViewChild("lineCanvas", { static: true }) lineCanvasRef: ElementRef<HTMLCanvasElement>;
      @Input('idname') idChartname: any;
      data: any;
      public myChart: Chart;
      @Input() resizedata: any;
      constructor(private dataservice: DataServiceService) {
        this.data = data[0].data1;
        this.dataservice.saveData(undefined);
      }
    
      ngOnInit() {
        if (this.resizedata != undefined) {
          this.idChartname = this.resizedata;
        }
      }
    
    
      ngAfterViewInit(): void {
        if (this.resizedata != undefined) {
          this.idChartname = this.resizedata;
        }
        this.dataservice.getFilteredData1.subscribe(message => {
    
          if (message) {
            var filteredData = this.filterFunction(message);
            this.renderChart(filteredData);
          }
          else {
            this.renderChart(this.data);
          }
        });
    
      }
    
    
      filterFunction(filterParams: any) {
        var newChartData = [...this.data]
        if (filterParams.searchTerm == '') {
          var filteredYearData = []
          filterParams.selectedYear.forEach(element => {
            var newItem = newChartData.filter(item => {
              return item['year'].toLowerCase().includes(element);
    
            })
            filteredYearData.push(newItem[0]);
    
          });
    
          return filteredYearData;
    
        }
        else if (filterParams.selectedYear == '' || filterParams.selectedYear == 'none') {
          var filterYear = newChartData.filter(item => {
            return item['year'].toLowerCase().includes(filterParams.searchTerm.toLowerCase());
          })
          var filterProfit = newChartData.filter(item => {
            return item['profit'].toLowerCase().includes(filterParams.searchTerm.toLowerCase());
          })
          if (filterProfit.length == 0) {
            filterProfit = filterYear
          }
          var filterValue = newChartData.filter(item => {
            return item['value'].toLowerCase().includes(filterParams.searchTerm.toLowerCase());
          })
          if (filterValue.length == 0) {
            filterValue = filterProfit
          }
          return filterValue;
        }
        else {
          var filteredYearData = []
          filterParams.selectedYear.forEach(element => {
            var newItem = newChartData.filter(item => {
              return item['year'].toLowerCase().includes(element);
    
            })
            filteredYearData.push(newItem[0]);
    
          });
    
          var filterYear = filteredYearData;
          var filterProfit = filterYear.filter(item => {
            return item['profit'].toLowerCase().includes(filterParams.searchTerm.toLowerCase());
          })
          var filterValue = filterYear.filter(item => {
            return item['value'].toLowerCase().includes(filterParams.searchTerm.toLowerCase());
          })
          var filterAllData
          if (filterProfit.length == 0 && filterValue.length != 0) {
            filterAllData = filterValue;
          }
          else if (filterProfit.length != 0 && filterValue.length == 0) {
            filterAllData = filterProfit;
          }
          else {
            filterAllData = filterValue;
          }
          return filterAllData;
        }
    
      }
    
      renderChart(data: any) {
        if (this.myChart) {
         this.myChart.destroy();
        }
    
        Chart.register(ChartDataLabels);
        Chart.defaults.plugins.datalabels.anchor = 'start';
        Chart.defaults.plugins.datalabels.align = 'start';
        Chart.defaults.scale.grid.drawOnChartArea = false;
        Chart.defaults.scale.grid.drawOnChartArea = false;
        Chart.defaults.plugins.legend.labels.usePointStyle = true;
    
    
    
    
        const lineCanvas: any = document.getElementById(this.idChartname);
        const targetCanvas: any = document.getElementById("chartAxis");
    
        if (lineCanvas != null && targetCanvas != null) {
    
          const ctx = targetCanvas.getContext("2d");
          let rectangleSet = false;
    
          const targetCtx = targetCanvas.getContext("2d");
    
          this.myChart = new Chart(ctx, {
            type: "line",
            data: {
              labels: data.map(x => x.year),
              datasets: [
                {
                  label: "Margin",
                  fill: false,
                  tension: 0.2,
                  backgroundColor: "rgba(75,192,192,0.4)",
                  borderColor: "#006C5B",
                  borderCapStyle: "butt",
                  borderDash: [],
                  borderDashOffset: 0.0,
                  borderJoinStyle: "miter",
                  pointBorderColor: "#006C5B",
                  pointBackgroundColor: "#006C5B",
                  pointBorderWidth: 1,
                  pointHoverRadius: 5,
                  pointHoverBackgroundColor: "#006C5B",
                  pointHoverBorderColor: "#003453",
                  pointHoverBorderWidth: 2,
                  pointRadius: 4,
                  pointHitRadius: 10,
                  spanGaps: false,
                  data: data.map(x => x.value),
                  borderWidth: 1
                }
              ]
            },
            /* chart options for design */
            options: {
              maintainAspectRatio: false,
                aspectRatio: 1, 
              responsive: true,
              scales: {
                x: {
                  ticks: {
    
                    // fontColor: 'black',
                    // fontStyle: 'bold',
                  },
                  grid: {
                    color: 'rgba(171,171,171,1)',
                    lineWidth: 2
                  },
                  title: {
                    display: true,
                    text: 'Year',
                    // fontColor: 'black',
                    font:{
                      family:'Helvetica Neue',
                      size:14,
                    }
    
                    //fontStyle: 'bold',
                  }
    
                },
                y: {
                  beginAtZero: true,
                  ticks: {
                    padding: 0,
                    // stepSize:10,
                    // fontColor: 'black',
                    //fontStyle: 'bold',
                    callback: function (value) {
                      return value + "%"
                    }
                  },
                  grid: {
                    color: 'rgba(171,171,171,1)',
                    lineWidth: 2
                  },
                  title: {
                    display: true,
                    text: 'Margin (%)',
                    // fontColor: 'black',
                    font:{
                      family:'Helvetica Neue',
                      size:14,
                    }
                    // fontStyle:'bold'
                  },
                }
              },
              layout: {
                padding: {
                  left: 0,
                  right: 0,
                  top: 0,
                  bottom: 0
                }
              },
              plugins: {  // ChartsJS DataLabels initialized here
                legend: {
                  display: true,
                  position: 'top',
                  labels: {
                    font: {
                      family:'Helvetica Neue'
                    }
                    // fontColor: '#333',
                  }
                },
                datalabels: {
                  formatter: function (value, context) {
                    return value + "%"
                  },
                  anchor: 'start',
                  align: 'right',
                  padding: {
                    left: 0,
                    right: 25,
                    top: 40,
                    bottom: 0
                  },
    
                  // formatter: Math.round,
                  font: {
                    // weight: 'bold',
                    size: 12,
                    family: 'Helvetica Neue'
                  },
                }
              },
              animation: {
                onComplete: function () {
                  if (!rectangleSet) {
                    const scale = window.devicePixelRatio;
                    const copyWidth = this.scales.y.width - 10;
                    const copyHeight = this.scales.y.chart.height + this.scales.y.top + 10;
    
                    targetCtx.scale(scale, scale);
                    targetCtx.canvas.width = copyWidth;
                    targetCtx.canvas.height = copyHeight;
                    targetCtx.canvas.style.width = copyWidth + 'px';
                    targetCtx.canvas.style.height = copyHeight + 'px';
                    targetCtx.drawImage(lineCanvas, 0, 0, copyWidth * scale, copyHeight * scale, 0, 0, copyWidth * scale, copyHeight * scale);
                    // ctx.clearRect(0, 0, copyWidth, copyHeight);
                    targetCtx.clearRect(0, 0, copyWidth, copyHeight);
                    rectangleSet = true;
                  }
                },
                onProgress: function () {
                  if (rectangleSet) {
                    var copyWidth = this.scales.y.width;
                    var copyHeight = this.scales.y.height + this.scales.y.top + 10;
                    this.ctx.clearRect(0, 0, copyWidth, copyHeight);
                  }
                },
              },
            },
    
          });
    
    
    
        } else {
          //this.myChart.update();
        }
      }
      scrollleft() {
        document.getElementById('scrollArea').scrollLeft += -30;
    
      }
      scrollright() {
        document.getElementById('scrollArea').scrollLeft += 30;
      };
    
      ngOnDestroy() {
        console.log("destroyed");
        this.myChart.destroy();
        //this.dataservice.saveData(undefined);
    
      }
    
    
    }

数据服务.service.ts

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable({
  providedIn: 'root'
})
export class DataServiceService {
  private filteredData = new BehaviorSubject<any>("");
  getFilteredData1: Observable<any>;
  constructor() {
    this.getFilteredData1 = this.filteredData.asObservable();
}

saveData(value) {
    this.filteredData.next(value);
}
}

boundry.component.html

<ion-card class="component-boundry content_class">

  <!-- Charts -->
  <ng-container *ngIf="renderComponent=='chart-animated-line'">
    <app-chart-animated-line></app-chart-animated-line>
  </ng-container>
  <ng-container *ngIf="renderComponent=='chart-bar'">
    <app-chart-bar idname="barChartId1"></app-chart-bar>

  </ng-container>
  <ng-container *ngIf="renderComponent=='chart-line'">
    <app-chart-line idname="lineChartId1"></app-chart-line>
  </ng-container>
  <ng-container *ngIf="renderComponent=='chart-multiple'">
    <app-chart-multiple></app-chart-multiple>
  </ng-container>
  <!--<ng-container *ngIf="renderComponent=='chart-pie'">
    <app-chart-pie></app-chart-pie>
  </ng-container>-->
  <ng-container *ngIf="renderComponent=='chart-bubble'">
    <app-chart-bubble></app-chart-bubble>
  </ng-container>


  <!-- file explorer -->
  <ng-container *ngIf="renderComponent=='file-attachment'">
    <app-attachment></app-attachment>
  </ng-container>


</ion-card>

出现错误

ERROR Error: Canvas is already in use. Chart with ID '11' must be destroyed before the canvas with ID 'chartAxis' can be reused.

因为它没有被 ngOnDestroy 方法破坏。使用 Angular 14 及以下版本和 Chart JS 2 可以正常工作。我不明白为什么 16 会发生这种情况。请帮忙解决。

angular chart.js html5-canvas ng2-charts
1个回答
0
投票

正如错误所述,您正在硬编码 ID

chartAxis
,因此多个 html 元素将重复相同的 ID,这是错误的,您可以尝试以下更改,我们通过附加
 将 ID 设置为动态-Axis
添加到您作为输入提供的动态 ID 的末尾,这可能会解决您的问题!

图表线.component.html

<!--chart container -->
<div id="container">
  <div id="scrollArea" class="CMI-ChartWrapper">
    <div class="CMI-Chart">
      <canvas baseChart #lineCanvas [id]="idChartname"></canvas>
    </div>
  </div>
  <canvas #targetCanvas [id]="idChartname+'-Axis'" height="400"></canvas>
</div>

图表线.component.ts

    ...
    Chart.defaults.plugins.legend.labels.usePointStyle = true;
    ...
    const lineCanvas: any = document.getElementById(this.idChartname);
    const targetCanvas: any = document.getElementById(`${this.idChartname}-Axis`); // <-- changed here!
    ...
© www.soinside.com 2019 - 2024. All rights reserved.