首页 > 解决方案 > ngAfterViewInit 调用太早

问题描述

我正在使用 am4chart,但我的控制台出现错误:html container not found

导致此错误的行是我的角度组件的这一行:
var chart = am4core.create("graphdiv", am4charts.XYChart);

控制台中的堆栈跟踪:

function createChild(htmlElement, classType) {
   var htmlContainer = $dom.getElement(htmlElement);
   if (htmlContainer) {
      ...
   }
   else {
      system.log("html container not found");
      throw new Error("html container not found");
   }
}

所以基本上我理解的是这一行 :var chart = am4core.create("graphdiv", am4charts.XYChart);在 DOM 中存在 graphdiv 元素之前执行,因为它没有找到 graphdiv HTML 元素。

所以我所做的是将创建图形的代码复制粘贴到ngAfterViewInit角度函数中,以确保在运行脚本之前执行创建我的 am4chart 图形的脚本以确保 DOM 存在

我的 afterViewInit 函数:

ngAfterViewInit(): void {
    console.log("CREATE GRAPH AFTER VIEW INIT")
    this.createGraph();
}

但我看到的是 console.log("CREATE GRAPH AFTER VIEW INIT") 实际上是在 DOM 完全呈现之前执行的,所以我所做的并没有解决我的错误html container not found

创建图():

createGraph() {

    console.log("CREATE GRAAAAPH");

    // Create chart instance
    am4core.ready(function() {

      var chart = am4core.create("graphdiv", am4charts.XYChart);
      chart.numberFormatter.numberFormat = this.currencyBefore + "#,####,###.##" + this.currencyAfter;

      chart.data = [];
      var cumul = 0;

      for (var i = 0; i < 37; i++) {
        if (i == 0) {
          chart.data.push({ "month": "", "units": 0 });
        } 
        else if (i == 1) {
          cumul = cumul + this.AppComponent.activeDetailedResults.prices.upfrontYear1 + this.AppComponent.activeDetailedResults.prices.monthlyPrice;
          chart.data.push({ "month": "M" + i, "units": cumul });
        } 
        else if (i == 13) {
          cumul = cumul + this.AppComponent.activeDetailedResults.prices.upfrontYear2 + this.AppComponent.activeDetailedResults.prices.monthlyPrice;
          chart.data.push({ "month": "M" + i, "units": cumul });
        } 
        else if (i == 25) {
          cumul = cumul + this.AppComponent.activeDetailedResults.prices.upfrontYear3 + this.AppComponent.activeDetailedResults.prices.monthlyPrice;
          chart.data.push({ "month": "M" + i, "units": cumul });
        } 
        else {
          cumul = cumul + this.AppComponent.activeDetailedResults.prices.monthlyPrice;
          chart.data.push({ "month": "M" + i, "units": cumul });
        }
      }

      // Create axes
      let categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
      categoryAxis.dataFields.category = "month";
      categoryAxis.renderer.labels.template.disabled = true
      categoryAxis.title.text = "Time (36 months)";

      let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
      valueAxis.renderer.labels.template.disabled = false;

      // Create series
      var series2 = chart.series.push(new am4charts.LineSeries());
      series2.name = "Units";
      series2.stroke = am4core.color("#551A8B");
      series2.strokeWidth = 3;
      series2.dataFields.valueY = "units";
      series2.dataFields.categoryX = "month";
      series2.fillOpacity = 0.5;
    });
  }

模板 :

<mat-card class="startcard">
   <p class="mat-title">Cumulative expenses: </p>
   <mat-card-content style="text-align: center;">
      <div fxLayout="row" fxLayoutAlign="space-around center">
         <div id="graphdiv"></div>
      </div>
   </mat-card-content>
</mat-card>

标签: angulardomgraph

解决方案


所以问题是该元素在组件创建时不可见,因为您正在等待 API 调用。您可以制作ViewChild一个二传手:

如果这是您的模板:

<mat-card class="startcard">
   <p class="mat-title">Cumulative expenses: </p>
   <mat-card-content style="text-align: center;">
      <div fxLayout="row" fxLayoutAlign="space-around center">
         <div #graphdiv></div>
      </div>
   </mat-card-content>
</mat-card>

您可以使您的组件如下:

private _div?: HTMLElement;

@ViewChild('graphdiv')
set graphDiv(div: ElementRef<HTMLElement>) {
  if (div && div.nativeElement && this._div !== div.nativeElement) {
    this._div = div.nativeElement;
    this.createGraph();
  }
}

createGraph() {
  am4core.ready(() => {
    const chart = am4core.create(this._div, am4charts.XYChart);
  });
}

这样您就不必在ngAfterViewInit钩子中或在 API 调用之后执行此操作。它会自动完成


推荐阅读