Cannot read properties of undefined for canvas in Angular component

Issue

I’m getting this error in runtime on my Angular 13 Ionic 6 application when run on two Android devices (Samsung Galaxy A32 and Xiaomi Rednote 8).

ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'getContext')

Instead of in ngAfterViewInit, I tried to instantiate everything in ngOnInit (with {static: true }) but it did not help.

This canvas is implemented in the whiteboard.component which is embedded in the chat.page.

What am I missing?

whiteboard template:

<ion-grid class="container no-scroll row h-100 container">
    <ion-row>
      <ion-col>
        <canvas #canvas class="no-scroll row" 
            (mousedown)="startDrawing($event)" 
            (touchstart)="startDrawing($event)" 
            (touchmove)="moved($event)" 
            (mousemove)="moved($event)" 
            (mouseup)="endDrawing()" 
            (touchend)="endDrawing()">
          </canvas>
      </ion-col>
    </ion-row>
...

whiteboard controller:

import { Component, ViewChild, AfterViewInit, ElementRef, OnInit } from '@angular/core';
import { NavController, IonContent  } from '@ionic/angular';
import { Platform, ToastController } from '@ionic/angular';
import { AwsFileUploadService } from '../../services/aws-file-upload.service'

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

  @ViewChild('canvas', { static: false }) 

  imgToast: HTMLElement; //For image hover capture

  //canvas: ElementRef<HTMLCanvasElement>;
  private canvas: ElementRef<HTMLCanvasElement> = {} as ElementRef<HTMLCanvasElement>;
  private ctx: CanvasRenderingContext2D;

  canvasElement: any;

  ngAfterViewInit() {
    
    this.ctx = this.canvas.nativeElement.getContext('2d'); // <<----- ERROR HERE
    this.ctx.canvas.width = this.plt.width() ;
    this.ctx.canvas.height = this.plt.height();
    //Need to upload the initial problem image here

    //Handle image hover if needed - set id of html image element to imgSection
    this.imgToast = document.getElementById('imgSection');
    this.imgToast.addEventListener('mousemove', ev => {
      this.showToastOnImage();
    });
  }
...

chat page template:

<ion-header>
  <ion-toolbar>
    <ion-title>chat</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-grid class="container h-100">
    <ion-row class="ion-justify-content-center row h-100 ion-no-padding">
        <ion-col size="12" id="content-text" class="ion-no-padding" style="height: 60%">
          <app-whiteboard style="height: 100%; width: 100%"></app-whiteboard>            
        </ion-col>
...

chat page module:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { FIREBASE_OPTIONS  } from '@angular/fire/compat';

import { IonicModule } from '@ionic/angular';

import { environment } from '../../environments/environment';

import { ChatPage } from './chat.page';
import { WhiteboardComponent } from './whiteboard/whiteboard.component';
import { RouterModule } from '@angular/router';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    RouterModule
  ],
  declarations: [ChatPage, WhiteboardComponent],
  providers: [{ provide: FIREBASE_OPTIONS, useValue: environment.firebaseConfig }],
  exports: [ChatPage]
})
export class ChatPageModule {}

Solution

As @ChrisHamilton refers, you have to add @ViewChild just before the canvas.

export class WhiteboardComponent implements AfterViewInit {

  imgToast: HTMLElement; //For image hover capture

  @ViewChild('canvas', { static: false }) canvas: ElementRef<HTMLCanvasElement>;

Answered By – N.F.

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published