How to fix Document is not defined when creating a canvas in Angular

53 Views Asked by At

In an Angular 17 component (NOT NODEJS) I am attempting to create a composite image using an HTML Canvas object. I create the canvas, load an image and apply it to the canvas, load a second image and apply that as well, then return the composite image.

The code itself is working, however, it is also throwing an error in the console.

ERROR ReferenceError: document is not defined
    at _TestComponent.eval...

Here is the code causing the error:

          ngOnInit() {
            const baseImageUrl = 'some url';
            const overlayImageUrl = 'some url';
            this.overlayImage(baseImageUrl, overlayImageUrl)
              .then((dataUrl: string) => {
                this.finalImageUrl = dataUrl;
              });  
          }
    
          async overlayImage(baseImageUrl: string, overlayImageUrl: string): Promise<string> {
            // Create an off-screen canvas
            const canvas = await document.createElement('canvas'); <-- ERROR OCCURS HERE
            const ctx = canvas.getContext('2d');
        
            // Load the base image
            const baseImage = await this.loadImage(baseImageUrl);
            canvas.width = baseImage.width;
            canvas.height = baseImage.height;
            ctx?.drawImage(baseImage, 0, 0);
        
            // Load the overlay image
            const overlayImage = await this.loadImage(overlayImageUrl);
            
            // Adjust the overlay image size and position as needed
            ctx?.drawImage(overlayImage, 0, 0, 630, 630);
        
            // Return the canvas as a data URL
            return canvas.toDataURL();
          }
        
          private loadImage(url: string): Promise<HTMLImageElement> {
            return new Promise((resolve, reject) => {
              const img = new Image();
              img.crossOrigin = 'Anonymous'; // This might be needed depending on CORS
              img.onload = () => resolve(img);
              img.onerror = reject;
              img.src = url;
            });
          }

While researching the issue, everything said that it means the canvas element has not loaded yet, so I added the "await" keyword, however this does not seem to help. Any thoughts?

As I said, while the function does actually work and produce an image, I'd prefer to not have this error.

TO REPRODUCE:

Angular CLI: 17.0.5 Node: 20.10.0 Package Manager: npm 10.5.0 OS: win32 x64

ng new test

Select SCSS for CSS Select Yes for SSR

cd test

Edit app.component.ts to match this:

import { Component, AfterViewInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent implements AfterViewInit {
  title = 'test';

  ngAfterViewInit(): void {
    const canvas = document.createElement('canvas');
  }
}

Save the project

from the command line, run the app:

ng s --o

ERROR ReferenceError: document is not defined

1

There are 1 best solutions below

1
Todd Davis On

After much ado I figured out the problem, and I am noting it here in case someone else has the same issue.

When I created my Angular app, I chose to enable SSR (Server side rendering). (I have never actually used it and was curious, and didn't realize it could affect things this way).

Long story short, since SSR is setup to run on the server, not the client, there is no Document object.

The fix was to remove SSR. The articles I found on how to remove SSR after the fact were inaccurate and confusing, so I just created a new Angular app without SSR and copied the necessary files over, took a few minutes tops.

Thank you to those that tried to help!