0

I try to integrate CalHeatMap https://cal-heatmap.com/docs/getting-started/quickstart

into my app:

import { Component } from '@angular/core';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { matWavingHandOutline } from '@ng-icons/material-icons/outline';
import CalHeatMap from 'cal-heatmap';

@Component({
  selector: 'app-dashboard-summary',
  standalone: true,
  imports: [NgIconComponent],
  templateUrl: './dashboard-summary.component.html',
  styleUrl: './dashboard-summary.component.css',
  viewProviders: [provideIcons({ matWavingHandOutline })],
})
export class DashboardSummaryComponent {
  ngOnInit() {
    let cal = new CalHeatMap();
    // cal.paint({});
  }
}

but when i try to call

cal.paint({});

i got:

ReferenceError: document is not defined

that comes that:

import {Selection, root} from "./selection/index.js";

export default function(selector) {
  return typeof selector === "string"
      ? new Selection([[document.querySelector(selector)]], [document.documentElement])
      : new Selection([[selector]], root);
}

so i tried:

import { JSDOM } from 'jsdom';
import { Selection, root } from './selection/index.js';

export default function(selector) {
  // Jeśli jest to Node.js, użyj jsdom do stworzenia dokumentu
  if (typeof document === "undefined") {
    const { window } = new JSDOM();
    global.document = window.document;
    global.window = window;
  }

  return typeof selector === "string"
      ? new Selection([[document.querySelector(selector)]], [document.documentElement])
      : new Selection([[selector]], root);
}

but i still get this error. Do you have any idea how to integrate it into the SSR app?

1 Answer 1

0

To integrate CalHeatMap into a Server-Side Rendered (SSR) Angular app, you'll need to consider the following:

Understanding the issue

  • The error ReferenceError: document is not defined occurs because CalHeatMap is using the DOM API, specifically document.querySelector, which is not available in the server-side environment. In an SSR app, the code runs on the server first, and thus document or window is not available.

Steps to Resolve

  1. Ensure Client-Side Execution: Since CalHeatMap is a client-side JavaScript library that interacts with the DOM, it must only be executed on the client-side, not during SSR. You can use Angular's isPlatformBrowser to check whether the code is running in the browser before initializing the CalHeatMap.

  2. Lazy Load or Dynamically Import: You can lazy-load CalHeatMap only on the client-side to prevent any SSR issues.

Solution: Modify the Component Code

Here’s how you can modify your code to ensure that CalHeatMap is initialized only on the client side.

Step 1: Modify ngOnInit to run only in the browser

Use isPlatformBrowser from Angular to ensure that the code is only run in the browser environment.

Step 2: Lazy-load CalHeatMap

To prevent errors during SSR, you can use dynamic imports or ngOnInit to conditionally load the library.

Here’s the updated version of your component:

import { Component, Inject, PLATFORM_ID, OnInit } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { matWavingHandOutline } from '@ng-icons/material-icons/outline';

@Component({
  selector: 'app-dashboard-summary',
  standalone: true,
  imports: [NgIconComponent],
  templateUrl: './dashboard-summary.component.html',
  styleUrls: ['./dashboard-summary.component.css'],
  viewProviders: [provideIcons({ matWavingHandOutline })],
})
export class DashboardSummaryComponent implements OnInit {

  constructor(@Inject(PLATFORM_ID) private platformId: Object) {}

  ngOnInit() {
    // Check if the platform is the browser
    if (isPlatformBrowser(this.platformId)) {
      // Dynamically import CalHeatMap in the browser only
      import('cal-heatmap').then((CalHeatMap) => {
        const cal = new CalHeatMap();
        cal.paint({
          // CalHeatMap configuration
        });
      }).catch(err => {
        console.error('Error loading CalHeatMap:', err);
      });
    }
  }
}

Explanation:

  1. isPlatformBrowser: This function checks if the app is running in the browser. If it is, we proceed to dynamically import and initialize CalHeatMap.
  2. Dynamic Import (import('cal-heatmap')): This ensures CalHeatMap is only loaded when running on the browser, thus preventing SSR issues.
  3. Error Handling: The dynamic import is wrapped in a then block to ensure that we can handle any errors if the library fails to load.

Additional Notes:

  • JSDOM: The approach you mentioned using jsdom is a workaround to simulate a DOM in a Node.js environment, but in most SSR scenarios with Angular, it's better to ensure that any client-side code is executed only in the browser, thus avoiding the need for jsdom.

  • SSR with Angular Universal: If you are using Angular Universal for SSR, the key is to isolate any browser-dependent code (like CalHeatMap) to only run on the client side, which is what the solution above accomplishes.

This approach should help you integrate CalHeatMap without issues on the server side.

Not the answer you're looking for? Browse other questions tagged or ask your own question.