1

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
            }
          }]
        }
      }
    });
  }
}
El Dude
  • 5,328
  • 11
  • 54
  • 101
  • 4
    View queries are set before the `ngAfterViewInit` callback is called and not before `ngOnInit`. Move the code to `ngAfterViewInit` or an Ionic specific life cycle hook. In the link you posted the code is in `ionViewDidEnter`. – frido Aug 23 '20 at 14:33
  • 1
    it just works: https://stackblitz.com/edit/ionic-qzjnfn?file=pages%2Fhome%2Fhome.ts – yaya Aug 23 '20 at 14:42
  • 1
    here is a list of possiblities about why viewChild doesn't work in your component : https://stackoverflow.com/questions/34947154/angular-2-viewchild-annotation-returns-undefined also try to create another component and use viewchild in it, to check if the problem is with your current component or you have problem with `viewChild` on your full app. – yaya Aug 23 '20 at 14:47
  • `ionViewDidEnter` did not work in ionic5 for unknown (to me) reason, `ngAfterViewInit` does. – El Dude Aug 24 '20 at 03:17
  • 1
    ionViewDidEnter only fires for “page” like components and won’t work for root app component etc afaik – Sergey Rudenko Aug 24 '20 at 04:31

1 Answers1

2

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())
    }
  };
  
}
Sergey Rudenko
  • 8,809
  • 2
  • 24
  • 51