Reputation: 11
I would like to use apexcharts and ng-apexcharts in angular 18 with SSR , i have this problem:
window is not defined at node_modules/apexcharts/dist/apexcharts.common.js (c:/Users/lakhm/Desktop/cod_manager/node_modules/apexcharts/dist/apexcharts.common.js:6:398992) at __require2 (C:/Users/lakhm/Desktop/cod_manager/.angular/vite-root/cod_manager/chunk-IOFBTDQ2.mjs:47:50) at eval (c:/Users/lakhm/Desktop/cod_manager/node_modules/ng-apexcharts/fesm2020/ng-apexcharts.mjs:4:24) at async instantiateModule (file:///C:/Users/lakhm/Desktop/cod_manager/node_modules/vite/dist/node/chunks/dep-cNe07EU9.js:55058:9
Upvotes: 1
Views: 1140
Reputation: 56054
window
and document
does not exist on the server. So when you use SSR in your angular application. The code rendering on the server has window
as undefined
. To solve this below are the steps to implement.
If you add the script "apexcharts/dist/apexcharts.min.js"
to angular.json
, it will run on both the server and the browser, which is bad since the package uses window. The solution for this will be to add the script only to main.server.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
import 'apexcharts/dist/apexcharts.min.js'; // <- changed here!
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err)
);
Then you can use the below method to convert the ng-apexcharts
package to support SSR. The most important part is that @defer
block runs only on the browser ( Thanks to Matthieu Riegler for the hint on reddit ), so we have to create two components, the first component is just a dummy wrapper component, that passes all the supported props from the main component to an inner component. The component is called
app-apx-chart-ssr
This component uses @defer
to wrap another component that contains the apex chart. The defer block ensures the content loads only on the browser and no error occour.
@defer() {
<app-apx-chart
[chart]="chart"
[annotations]="annotations"
[colors]="colors"
[dataLabels]="dataLabels"
[series]="series"
[stroke]="stroke"
[labels]="labels"
[legend]="legend"
[markers]="markers"
[noData]="noData"
[fill]="fill"
[tooltip]="tooltip"
[plotOptions]="plotOptions"
[responsive]="responsive"
[xaxis]="xaxis"
[yaxis]="yaxis"
[forecastDataPoints]="forecastDataPoints"
[grid]="grid"
[states]="states"
[title]="title"
[subtitle]="subtitle"
[theme]="theme"
[autoUpdateSeries]="autoUpdateSeries"
(chartReady)="chartReady.next($event)"
/>
}
import { Component, EventEmitter, Input, Output } from '@angular/core';
import {
ApexTheme,
NgApexchartsModule,
ApexChart,
ApexAnnotations,
ApexDataLabels,
ApexAxisChartSeries,
ApexNonAxisChartSeries,
ApexStroke,
ApexLegend,
ApexMarkers,
ApexNoData,
ApexFill,
ApexTooltip,
ApexPlotOptions,
ApexResponsive,
ApexXAxis,
ApexYAxis,
ApexForecastDataPoints,
ApexGrid,
ApexStates,
ApexTitleSubtitle,
} from 'ng-apexcharts';
import { ApxChartComponent } from './apx-chart/apx-chart.component';
@Component({
selector: 'app-apx-chart-ssr',
standalone: true,
imports: [ApxChartComponent],
templateUrl: './apx-chart-ssr.component.html',
})
export class ApxChartSsrComponent {
@Input() chart!: ApexChart;
@Input() annotations!: ApexAnnotations;
@Input() colors!: any[];
@Input() dataLabels!: ApexDataLabels;
@Input() series!: ApexAxisChartSeries | ApexNonAxisChartSeries;
@Input() stroke!: ApexStroke;
@Input() labels!: string[];
@Input() legend!: ApexLegend;
@Input() markers!: ApexMarkers;
@Input() noData!: ApexNoData;
@Input() fill!: ApexFill;
@Input() tooltip!: ApexTooltip;
@Input() plotOptions!: ApexPlotOptions;
@Input() responsive!: ApexResponsive[];
@Input() xaxis!: ApexXAxis;
@Input() yaxis!: ApexYAxis | ApexYAxis[];
@Input() forecastDataPoints!: ApexForecastDataPoints;
@Input() grid!: ApexGrid;
@Input() states!: ApexStates;
@Input() title!: ApexTitleSubtitle;
@Input() subtitle!: ApexTitleSubtitle;
@Input() theme!: ApexTheme;
@Input() autoUpdateSeries = true;
@Output() chartReady = new EventEmitter();
}
Then in the inner component app-apx-chart
you will see the important import imports: [NgApexchartsModule]
(This import loads only on the browser due to defer, where window exists, without defer, you will get window undefined error).
Other than this the inner component serves to use the import only on browser and not much, same as the above component, just to pass props to the package component (apx-chart
).
import { Component, EventEmitter, Input, Output } from '@angular/core';
import {
ApexTheme,
NgApexchartsModule,
ApexChart,
ApexAnnotations,
ApexDataLabels,
ApexAxisChartSeries,
ApexNonAxisChartSeries,
ApexStroke,
ApexLegend,
ApexMarkers,
ApexNoData,
ApexFill,
ApexTooltip,
ApexPlotOptions,
ApexResponsive,
ApexXAxis,
ApexYAxis,
ApexForecastDataPoints,
ApexGrid,
ApexStates,
ApexTitleSubtitle,
} from 'ng-apexcharts';
@Component({
selector: 'app-apx-chart',
standalone: true,
imports: [NgApexchartsModule],
templateUrl: './apx-chart.component.html',
styleUrl: './apx-chart.component.css',
})
export class ApxChartComponent {
@Input() chart!: ApexChart;
@Input() annotations!: ApexAnnotations;
@Input() colors!: any[];
@Input() dataLabels!: ApexDataLabels;
@Input() series!: ApexAxisChartSeries | ApexNonAxisChartSeries;
@Input() stroke!: ApexStroke;
@Input() labels!: string[];
@Input() legend!: ApexLegend;
@Input() markers!: ApexMarkers;
@Input() noData!: ApexNoData;
@Input() fill!: ApexFill;
@Input() tooltip!: ApexTooltip;
@Input() plotOptions!: ApexPlotOptions;
@Input() responsive!: ApexResponsive[];
@Input() xaxis!: ApexXAxis;
@Input() yaxis!: ApexYAxis | ApexYAxis[];
@Input() forecastDataPoints!: ApexForecastDataPoints;
@Input() grid!: ApexGrid;
@Input() states!: ApexStates;
@Input() title!: ApexTitleSubtitle;
@Input() subtitle!: ApexTitleSubtitle;
@Input() theme!: ApexTheme;
@Input() autoUpdateSeries = true;
@Output() chartReady = new EventEmitter();
}
<apx-chart
[chart]="chart"
[annotations]="annotations"
[colors]="colors"
[dataLabels]="dataLabels"
[series]="series"
[stroke]="stroke"
[labels]="labels"
[legend]="legend"
[markers]="markers"
[noData]="noData"
[fill]="fill"
[tooltip]="tooltip"
[plotOptions]="plotOptions"
[responsive]="responsive"
[xaxis]="xaxis"
[yaxis]="yaxis"
[forecastDataPoints]="forecastDataPoints"
[grid]="grid"
[states]="states"
[title]="title"
[subtitle]="subtitle"
[theme]="theme"
[autoUpdateSeries]="autoUpdateSeries"
(chartReady)="chartReady.next($event)"
></apx-chart>
Upvotes: 2