Reputation: 351
I am using am4chart and I got an error in my console : html container not found
.
The line which is causing this error is this line of my angular component :
var chart = am4core.create("graphdiv", am4charts.XYChart);
stacktrace in console :
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");
}
}
So basically what I understand is that this line : var chart = am4core.create("graphdiv", am4charts.XYChart);
is executed BEFORE the graphdiv element exists in the DOM, because it didn't found the graphdiv HTML element.
So what I did is that I copy paste my code which creates the graph into the ngAfterViewInit
angular function, to make sure that the script which creates my am4chart graph is executed AFTER to make sure that DOM exists before running the script
My afterViewInit function :
ngAfterViewInit(): void {
console.log("CREATE GRAPH AFTER VIEW INIT")
this.createGraph();
}
But what I see is that the console.log("CREATE GRAPH AFTER VIEW INIT") is in fact executed BEFORE that the DOM is fully rendered, so what I did doesn't slve my error html container not found
createGraph() :
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;
});
}
template :
<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>
Upvotes: 0
Views: 1938
Reputation: 71941
So the issue is that the element is not visible at component creation, because you are waiting for API calls. You can make the ViewChild
a setter:
If this is your template:
<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>
You can make your component the following:
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);
});
}
This way you don't have to do it in the ngAfterViewInit
hook or after the API call. It will be done automatically
Upvotes: 1
Reputation: 2992
You can try to move your code to ngAfterViewChecked
method. As another try is to use setTimeout
with 0 time and your code inside.
Upvotes: 1