angular - 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>
解决方案
所以问题是该元素在组件创建时不可见,因为您正在等待 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 调用之后执行此操作。它会自动完成
推荐阅读
- ruby - 用于计算数据集中任何给定距离的经过时间的算法或公式?
- python - 以编程方式运行 ipyvuetify 按钮
- python - 在 .query 函数中使用变量
- ios - UIApplication.isIdleTimerDisabled 中的崩溃
- java - 如何在另一个类中使用一个类?
- svelte - 你可以在 svelte 条件语句中添加可选的 _outer_ 标签吗?
- android - 如何动画隐藏和显示功能
- javascript - NextJS getInitialProps
- python - SpriteCollide 每次碰撞只运行一次
- angular - 在 Angular Overlay 上动态更改 ElementRef 以重新定位它?