Search code examples
javascripttypescripthtml5-canvasglobal-variablesundefined

Typescript/JavaScript: Why is my canvas and canvas context undefined, even though I initialized them?


I am playing around with a grid in Typescript in Angular. I defined my canvas and context as global variables and initialize them in the ngOnInit(). Afterwards, I call a function that uses the canvas and the context. In the console, I get the following error: ERROR TypeError: Cannot read property 'ctxGrid' of undefined at resetGrid (grid-draw.component.ts:69)

I do not know what I am doing wrong as I have initialized both variables. This may be something I am unaware of. Thank you. Here is my code:

import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';

@Component({
  selector: 'app-grid-draw',
  templateUrl: './grid-draw.component.html',
  styleUrls: ['./grid-draw.component.css']
})
export class GridDrawComponent implements OnInit {

  constructor() { }

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



  shapes;
  canvas;
  ctxGrid;
  ngOnInit(): void {
    const animDelay = 5;
    const startNodeColor = "#FF3600";
    const endNodeColor = "#00AB5C";

    //initialize array an grid
    this.shapes = new Array(100);
    for (let i = 0; i < this.shapes.length; i++) { this.shapes[i] = new Array(100) };
    this.canvas = <HTMLCanvasElement>document.getElementById('myCanvas');
    this.ctxGrid = this.canvas.getContext('2d');
    resetGrid();

    //walls
    this.canvas.addEventListener('mousemove', function (e) {
      const rect = this.canvas.getBoundingClientRect();

      let cx = e.clientX - rect.left;
      let cy = e.clientY - rect.top;
      //draw_erase_walls(e, cx, cy);
    })

    //single click for start and end nodes
    this.canvas.addEventListener('mousedown', function (e) {
      const rect = this.canvas.getBoundingClientRect();
      let cx = e.clientX - rect.left;
      let cy = e.clientY - rect.top;
      //changeStart(cx, cy);
      //changeEnd(cx, cy);
    })

    function resetGrid() {
      this.ctxGrid.lineWidth = 0.05;
      //grid with rectangles
      for (let i = 0; i < this.shapes.length; i++) {
        for (let j = 0; j < this.shapes[i].length; j++) {
          //variables
          let x = i * 20;
          let y = j * 20;
          let l = 20, w = 20;
          let type = ""

          //a* algorithm info
          let F = 0;
          let G = 0;
          let H = 0;
          if (i == 4 && j == 4) {    
            this.ctxGrid.fillStyle = startNodeColor;
            type = "Start"
            //draw it
            this.ctxGrid.strokeRect(x, y, l, w);
            this.ctxGrid.fillRect(x, y, l, w);
          }

          else if (i == (this.canvas.width / 20 - 5) && j == (this.canvas.height / 20 - 5)) {
            this.ctxGrid.fillStyle = endNodeColor;

            type = "End"
            //draw it
            this.ctxGrid.strokeRect(x, y, l, w);
            this.ctxGrid.fillRect(x, y, l, w);
          }

          else {
            //push the default square info
            this.ctxGrid.fillStyle = "#000000"
            //draw it
            this.ctxGrid.strokeRect(x, y, l, w);
          }
          this.shapes[i][j] = { x, y, l, w, i, j, type, F, G, H };  //x and y are grid coordinates, and i j is the index in array the square object is in
        }
      }
      //a_star_search();
    }
  }
}

Solution

  • Yea my friend it's the same reason I decided to right here let me try to fix those for you..

    import { Component, ViewChild, ElementRef, OnInit } from '@angular/core';
    
    @Component({
        selector: 'app-grid-draw',
        templateUrl: './grid-draw.component.html',
        styleUrls: ['./grid-draw.component.css']
    })
    export class GridDrawComponent implements OnInit {
    
        constructor() { }
    
        @ViewChild('canvas', { static: true })
    
    
    
        shapes;
        canvas;
        ctxGrid;
        ngOnInit(): void {
            const animDelay = 5;
            const startNodeColor = "#FF3600";
            const endNodeColor = "#00AB5C";
    
            //initialize array an grid
            this.shapes = new Array(100);
            for (let i = 0; i < this.shapes.length; i++) { this.shapes[i] = new Array(100) };
            this.canvas = <HTMLCanvasElement>document.getElementById('myCanvas');
            this.ctxGrid = this.canvas.getContext('2d');
            resetGrid();
            //walls
            this.canvas.addEventListener('mousemove', (e) => {
                const rect = this.canvas.getBoundingClientRect();
    
                let cx = e.clientX - rect.left;
                let cy = e.clientY - rect.top;
                //draw_erase_walls(e, cx, cy);
            })
    
            //single click for start and end nodes
            this.canvas.addEventListener('mousedown', (e) => {
                const rect = this.canvas.getBoundingClientRect();
                let cx = e.clientX - rect.left;
                let cy = e.clientY - rect.top;
                //changeStart(cx, cy);
                //changeEnd(cx, cy);
            })
    
        }
    
        resetGrid(): void {
            this.ctxGrid.lineWidth = 0.05;
            //grid with rectangles
            for (let i = 0; i < this.shapes.length; i++) {
                for (let j = 0; j < this.shapes[i].length; j++) {
                    //variables
                    let x = i * 20;
                    let y = j * 20;
                    let l = 20, w = 20;
                    let type = ""
    
                    //a* algorithm info
                    let F = 0;
                    let G = 0;
                    let H = 0;
                    if (i == 4 && j == 4) {
                        this.ctxGrid.fillStyle = startNodeColor;
                        type = "Start"
                        //draw it
                        this.ctxGrid.strokeRect(x, y, l, w);
                        this.ctxGrid.fillRect(x, y, l, w);
                    }
    
                    else if (i == (this.canvas.width / 20 - 5) && j == (this.canvas.height / 20 - 5)) {
                        this.ctxGrid.fillStyle = endNodeColor;
    
                        type = "End"
                        //draw it
                        this.ctxGrid.strokeRect(x, y, l, w);
                        this.ctxGrid.fillRect(x, y, l, w);
                    }
    
                    else {
                        //push the default square info
                        this.ctxGrid.fillStyle = "#000000"
                        //draw it
                        this.ctxGrid.strokeRect(x, y, l, w);
                    }
                    this.shapes[i][j] = { x, y, l, w, i, j, type, F, G, H };  //x and y are grid coordinates, and i j is the index in array the square object is in
                }
            }
            //a_star_search();
        }
    }
    

    Hope it works this way. Cause arrow functions doesn't have a context it's usings your components context inside move or down functions =) (Atleast it should :)