Search code examples
htmlangularionic-frameworkionic4ionic5

Ionic5 component get @ViewChild not working


I am trying to implement a plot into a ionic component based on these examples: https://enappd.com/blog/charts-in-ionic-4-apps-and-pwa-part-1/52/

I simply copy & paste the stuff into a component but when I run the component the @ViewChild will not be found. I tried it with the ionic native @ViewChild option and with document.getElementByID but both will not return the plot element. this.barChart. will be undefined and crash the createBarChart function.

I have the feeling that because it is a component the document.getElementByID searches the parent document tree and not the component document.

HTML:

<ion-content>
  <ion-card class="welcome-card">
    <ion-card-header>
      <ion-card-subtitle>Number of Viewers per season for</ion-card-subtitle>
      <ion-card-title>Game of Thrones</ion-card-title>
    </ion-card-header>
    <ion-card-content>
      <canvas #barChart></canvas>
    </ion-card-content>
  </ion-card>
</ion-content>

TS

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { Chart } from 'chart.js';

@Component({
  selector: 'app-plot',
  templateUrl: './plot.component.html',
  styleUrls: ['./plot.component.scss'],
})
export class PlotComponent implements OnInit {

  @ViewChild('barChart') barChart: ElementRef;

  bars: any;
  colorArray: any;

  constructor() { }

  ngOnInit() {
    this.createBarChart();
  }

  createBarChart() {
    this.bars = new Chart(this.barChart.nativeElement, {
      type: 'bar',
      data: {
        labels: ['S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7', 'S8'],
        datasets: [{
          label: 'Viewers in millions',
          data: [2.5, 3.8, 5, 6.9, 6.9, 7.5, 10, 17],
          backgroundColor: 'rgb(38, 194, 129)', // array should have same number of elements as number of dataset
          borderColor: 'rgb(38, 194, 129)', // array should have same number of elements as number of dataset
          borderWidth: 1
        }]
      },
      options: {
        scales: {
          yAxes: [{
            ticks: {
              beginAtZero: true
            }
          }]
        }
      }
    });
  }
}

Solution

  • As @fridoo mentioned you are trying to initialize a dom element inside ngOnInit hook, whereas the template code has not been initialized yet.

    Specifically with HTMLCanvasElement it is best to use Ionic's IonViewDidEnter hook as then your Canvas element and other elements (such as ion-header) will fully initialize and you will be able to reliably refer to the element as well is its offsets.

    You can see it this way:

    import { Component, ViewChild, ElementRef } from '@angular/core';
    import { AlertController } from '@ionic/angular';
    @Component({
      selector: 'my-page',
      templateUrl: './my-page.component.html',
      styleUrls: ['./my-page.component.css']
    })
    export class MyPageComponent {
    
      @ViewChild('barChart') barChart: ElementRef;
    
      constructor() {}
    
      ngOnInit() {
        console.log(this.barChart)
        if (this.barChart) {
          console.log(this.barChart.nativeElement.getBoundingClientRect())
        }
      };
    
      ngAfterViewInit() {
        console.log(this.barChart)
        if (this.barChart) {
          console.log(this.barChart.nativeElement.getBoundingClientRect())
        }
      };
    
      ionViewDidEnter() {
        console.log(this.barChart)
        if (this.barChart) {
          console.log(this.barChart.nativeElement.getBoundingClientRect())
        }
      };
      
    }