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?
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, specificallydocument.querySelector
, which is not available in the server-side environment. In an SSR app, the code runs on the server first, and thusdocument
orwindow
is not available.
Steps to Resolve
-
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’sisPlatformBrowser
to check whether the code is running in the browser before initializing the CalHeatMap. -
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:
isPlatformBrowser
: This function checks if the app is running in the browser. If it is, we proceed to dynamically import and initialize CalHeatMap.- Dynamic Import (
import('cal-heatmap')
): This ensures CalHeatMap is only loaded when running on the browser, thus preventing SSR issues. - 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 forjsdom
. -
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.