title: Angular 2+ get reference of dynamically-generated HTML elements for canvas manipulation
body: <p>I have drawing service in Angular with several methods that take various drawing-related parameters and a parameter that references the dynamically-generated Id of a canvas element like the drawLizard method herein:</p>
  
  <p>drawing.service:</p>
  
  <pre><code>import { Injectable } from '@angular/core';
  import { Genotype } from './genotype.model';
  
  @Injectable({
    providedIn: 'root'
  })
  export class DrawingService {
    constructor() {
    }
  
    drawArc(x: number, y: number, radius: number, startAngle:number, endAngle: number, elementId: string, color: string, fillStatus: boolean) {
      let canvas = &lt;HTMLCanvasElement&gt; document.getElementById(elementId);
      // console.log(canvas); //TODO fix
      if (canvas) {
        let ctx = canvas.getContext('2d');
        ctx.beginPath();
        ctx.arc(x, y, radius, startAngle, endAngle);
        if (fillStatus) {
          ctx.fillStyle = color;
          ctx.fill();
        } else {
          ctx.lineWidth = 1;
          ctx.strokeStyle = color;
          ctx.stroke();
        }
      } else {
        //TODO handle error here
      }
    }
  
    drawTriangle(x1: number, y1: number, x2: number, y2: number, x3: number, y3:number, elementId: string, color: string, fillStatus: boolean){
  
      // let canvasElementTest = &lt;HTMLCanvasElement&gt;document.getElementById("first");
      // console.log(canvasElementTest); //TODO fix
      let canvasElement = &lt;HTMLCanvasElement&gt;document.getElementById(elementId);
      // let canvasElement: Element = &lt;HTMLCanvasElement&gt;document.getElementsByClassName(elementId);
      console.log(canvasElement); //TODO fix
      if (canvasElement){
        let context = canvasElement.getContext("2d");
        context.beginPath();
        context.moveTo(x1, y1);
        context.lineTo(x2, y2);
        context.lineTo(x3, y3);
        context.closePath();
        context.lineWidth = 1;
        context.strokeStyle = color;
        context.stroke();
        if(fillStatus){
          context.fillStyle = color;
          context.fill();
      }
    } else{
      //TODO throw/handle error here
    }
  }
  
  drawEllipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle:number, endAngle:number, elementId: string, color: string, fillStatus: boolean){
    let canvas = &lt;HTMLCanvasElement&gt; document.getElementById(elementId);
    if(canvas){
      let ctx = canvas.getContext('2d');
      // ctx.setLineDash([])
      ctx.beginPath();
      ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, false); //x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise
      ctx.stroke();
      if(fillStatus){
        ctx.fillStyle = color;
        ctx.fill();
      }
      ctx.closePath();
    } else{
      //TODO handle error here
    }
  
  }
  
  drawLizard(canvasId: string, genotype: Genotype){
    //head
    this.drawTriangle(37.5,87.5,37.5,112.5,25,100, canvasId,"black", true);
    this.drawArc(37.5, 100, 12.5, 3*Math.PI/2, Math.PI/2, canvasId,"black", true);
    // console.log("mark got here, too"); //TODO fix
  
    //eyes
    this.drawArc(37.5, 106.25, 2, 0, 2*Math.PI, canvasId,"white", true);
    this.drawArc(37.5, 93.75, 2, 0, 2*Math.PI, canvasId,"white", true);
    this.drawArc(37.5, 106.25, 0.5, 0, 2*Math.PI, canvasId,"black", true);
    this.drawArc(37.5, 93.75, 0.5, 0, 2*Math.PI, canvasId,"black", true);
  
    //body
    this.drawEllipse(100, 100, 50, 10, 0, 0, 2 * Math.PI, canvasId, 'black', true);
  
    //legs
    this.drawEllipse(140, 110, 30, 3, 0.65, 0, 2 * Math.PI, canvasId, 'black', true);
    this.drawEllipse(140, 90, 30, 3, 0.9+Math.PI/2, 0, 2 * Math.PI, canvasId, 'black', true);
    this.drawEllipse(70, 110, 30, 3, -Math.PI/6, 0, 2 * Math.PI, canvasId, 'black', true);
    this.drawEllipse(70, 90, 30, 3, Math.PI/6, 0, 2 * Math.PI, canvasId, 'black', true);
  
    //random polka dots
    // let colorArray = new Array&lt;string&gt;("blue", "pink", "orange", "#FF00FF", "red", "#00FFFF", "#800000", "#00FF00", "#008000", "#00FFFF", "#008080", "#BFBFFE", "#800080");
    // this.drawArc(77, 95, 3, 0, 2*Math.PI, canvasId, colorArray[Math.floor(Math.random()*colorArray.length)], true);
  
    this.drawArc(60, 101, 3, 0, 2*Math.PI, canvasId, genotype.getAllele1(), true);
    this.drawArc(85, 102, 3, 0, 2*Math.PI, canvasId, genotype.getAllele2(), true);
    this.drawArc(109, 94, 3, 0, 2*Math.PI, canvasId, genotype.getAllele1(), true);
    this.drawArc(120, 102, 3, 0, 2*Math.PI, canvasId, genotype.getAllele2(), true);
  }
  }
  </code></pre>
  
  <p>I have several such canvas elements dynamically generated in my lizard-display.component.html:</p>
  
  <pre><code>...
  &lt;mat-card small class="example-card" *ngFor="let individual of individuals; let i=index"&gt;
  ...
  &lt;canvas id="lizard-canvas{{i}}" width="200" height="200" style="border:1px solid #c3c3c3;"&gt;
      Your browser does not support the canvas element.
  &lt;/canvas&gt;
  ...
  &lt;/mat-card&gt;
  ...
  </code></pre>
  
  <p>In my lizard-display.component.ts file, I generate an example lizard and attempt to draw it:</p>
  
  <pre><code>import { OnInit, Component } from '@angular/core';
  import { DrawingService } from '../drawing.service';
  import { Genotype } from '../genotype.model';
  import { Gene } from '../gene.model';
  import { Organism } from '../organism.model';
  import { ColorNameService } from '../color-name.service';
  import { IndividualGenerationService } from '../individual-generation.service';
  
  @Component({
    selector: 'app-lizard-display',
    templateUrl: './lizard-display.component.html',
    styleUrls: ['./lizard-display.component.css'],
    providers: [DrawingService]
  })
  export class LizardDisplayComponent implements OnInit {
    private individuals: Array&lt;Organism&gt; = new Array&lt;Organism&gt;();
  
    constructor(private ds: DrawingService, private cns: ColorNameService, private individualGenService: IndividualGenerationService) { }
  
    ngOnInit() {
  
        //TODO delete me after fleshed out more
        let testIndividual: Organism = this.individualGenService.makeIndividual("green", "blue");
        let genotype: Genotype = testIndividual.getGeneByName("spot color").getGenotype();
        this.individuals.push(testIndividual);
  
        this.ds.drawLizard('lizard-canvas0', genotype);
    } 
  }
  </code></pre>
  
  <p>Lots of solutions out there (e.g.,<a href="https://stackoverflow.com/questions/40535014/angular2-how-to-get-reference-of-html-elements-generated-dynamically">here</a>) seem to work for folks who don't have to then apply <code>.getContext('2d')</code> on their element.</p>
  
  <p>As it is, I can confirm that canvasElement (at least in the <code>drawTriangle</code> method  called by <code>drawLizard</code> in the drawing service) using these solutions on SO is null.</p>
  
  <p>I'm hoping for Angular2+-like solutions to this issue.</p>
  
  <p>A functional, reasonably minimal reproducible example can be found here:</p>
  
  <pre><code>git clone https://github.com/Atticus29/population-fragmentation.git
  cd population-fragmentation
  git checkout dynamicCanvasSO
  npm install
  ng serve
  </code></pre>
  
  <p>Navigate to <a href="http://localhost:4200/" rel="nofollow noreferrer">http://localhost:4200/</a> in a browser</p>