离开带有 Hicharts 图表的页面时无法读取未定义的 属性 'forExport'

Cannot read property 'forExport' of undefined when leaving the page with Hicharts chart

我可以在一个非常简单的 Angular-Hicharts 应用程序中重现该问题。当我从服务器检索数据,然后离开带有 Highcharts 图表的页面时,出现此错误:

Cannot read property 'forExport' of undefined at a.destroy (highcharts.js:394) at HighchartsChartComponent.ngOnDestroy (highcharts-angular.js:44)

毫不奇怪,HighchartsChartComponent.ngOnDestroy

ngOnDestroy() {
    if (this.chart) { // #56
        this.chart.destroy();
        this.chart = null;
    }
}

这是最简单的例子:

export class WeatherForecastComponent implements OnInit {
  highcharts: typeof Highcharts = Highcharts;
  chartOptions: Highcharts.Options = { ... };

constructor(private http: HttpClient) { }

ngOnInit(): void {
    this.http.get<any[]>(`http://localhost:57432/WeatherForecast`, {
        headers: new HttpHeaders({
            "Content-Type": "application/json"
    })})
    .subscribe(result => {
        let categories: string[] = [];
        let dataSeries: number[] = [];
        for (let i = 0; i < result.length; i++) {
            categories.push(result[i]['location']);
            dataSeries.push(result[i]['temperatureC']);
        }
        this.chartOptions.xAxis = {
            categories: categories
        };
        this.chartOptions.series = [{
          type: "column",
          name: "Cities",
          data: dataSeries
        }];
     this.highcharts.chart('container', this.chartOptions);
     }, error => {
         console.log(error);
     });
  }
}

我在 Stack Overflow 和 Github 上看到了许多类似的问题。我明白根本原因是它试图删除已经删除的图表。但是他们都在删除(或试图删除)应用程序代码中的图表。

我尝试删除 ngOnDestroy() 中的图表或取消订阅 - 但没有区别。

这是repo,包括服务器端代码。

请注意,当它是“硬编码”图表时,不会发生问题。

Angular: 12.2;高字符 9.2.2; Highcharts-Angular: 2.10.0

在您的代码中有一个不必要的东西导致了这个问题。
您不必破坏此处的图表参考。当您删除它时,一切都按预期工作。

ngOnDestroy() {
   this.chartRef.destroy()
}

另请注意,在 subscribe 中创建第二个图表并不理想。 Insted 使用 updateFlag.

this.highcharts.chart('container', this.chartOptions);

演示:https://stackblitz.com/edit/highcharts-angular-basic-line-nxzqvy

主要有两点不起作用。 但在我解释之前,让我们先看看负责在包装器中创建图表的代码。

ngOnChanges(changes) {
        const update = changes.update && changes.update.currentValue;
        if (changes.options || update) {
            this.wrappedUpdateOrCreateChart();
            if (update) {
                this.updateChange.emit(false); // clear the flag after update
            }
        }
    }
    wrappedUpdateOrCreateChart() {
        if (this.runOutsideAngular) {
            this._zone.runOutsideAngular(() => {
                this.updateOrCreateChart();
            });
        }
        else {
            this.updateOrCreateChart();
        }
    }
    updateOrCreateChart() {
        if (this.chart && this.chart.update) {
            this.chart.update(this.options, true, this.oneToOne || false);
        }
        else {
            this.chart = this.Highcharts[this.constructorType || 'chart'](this.el.nativeElement, this.options, this.callbackFunction || null);
            // emit chart instance on init
            this.chartInstance.emit(this.chart);
        }
    }

如您所见,一切都发生在ngOnChanges 钩子中。如果检测到更改,则会触发一系列方法以最终更新图表。

现在不起作用的东西:

  1. 如果 subscribe 中发生任何异步代码(例如 HTTP 请求 - 在我的示例中被 setTimeout 模拟),您必须手动设置包装器应该通过设置 updateFlag 来更新。因为 Angular 没有检测到 chartOptions.

    中某些属性的变化
  2. 如果您像下面的代码片段一样将属性分配给 chartOptions,请将 oneToOne 标志设置为 true

     this.chartOptions.xAxis = {
            categories: categories
          };
          this.chartOptions.series = [{
            type: "column",
            name: "Cities",
            data: dataSeries
          }];

文档:https://github.com/highcharts/highcharts-angular#options-details
演示:https://stackblitz.com/edit/highcharts-angular-basic-line-vwrrmx