jake clark
jake clark

Reputation: 69

ReferenceError: window is not defined while using Apex charts in angular SSR

I have created an Angular SSR app, there I have installed NgApexchartsModule module, after this when I try to run the below command I am facing this issue:

 command: ng run abc:serve-ssr

ReferenceError: window is not defined
    at Object.ujAs (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:230211:345871)
    at __webpack_require__ (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:20:30)
    at Module.CV0D (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:91098:68)
    at __webpack_require__ (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:20:30)
    at Module.ZAI4 (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:127394:72)
    at __webpack_require__ (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:20:30)
    at Module.24aS (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:55649:69)
    at __webpack_require__ (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:20:30)
    at Module.K011 (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:102532:80)
    at __webpack_require__ (D:\EXPERIMENTAL\SSR\abc\dist\abc\server\main.js:20:30)

A server error has occurred.
node exited with 1 code.
connect ECONNREFUSED 127.0.0.1:59450

Configuration:

app.module.ts

    imports: [
    BrowserModule,
    BrowserAnimationsModule,
    MaterialModuleComponents,
    NgbModule,
    NgxGaugeModule,
    HttpClientModule,
    NgxMatIntlTelInputModule,
    Ng2SearchPipeModule,
    NgxSpinnerModule,
    **NgApexchartsModule**,
    NgpImagePickerModule,
    NgxFileDragDropModule,
    MatDatetimepickerModule,
    MatNativeDatetimeModule,
    NgxSkeletonLoaderModule.forRoot(),
    JoyrideModule.forRoot(),
    BrowserModule.withServerTransition({ appId: 'serverApp' })
  ],

Server.ts

import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';

import {enableProdMode} from '@angular/core';

enableProdMode();


// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/abc/browser');
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

  const domino = require('domino');
  const win = domino.createWindow(indexHtml);
  // mock
  global['window'] = win;
  global['document'] = win.document;
  global['navigator'] = win.navigator;


  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));

  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

  return server;
}

function run(): void {
  const port = process.env.PORT || 4000;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';

app.server.module.ts

import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
  imports: [
    AppModule,
    ServerModule,
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

I tried with window element also which I have motioned in the above code but still facing the same issue. Thanks in advance.

Upvotes: 0

Views: 2552

Answers (1)

federico scamuzzi
federico scamuzzi

Reputation: 3778

Your problem is you're using SSR .. and in the server window doesn't EXIST ... so you can basically check for which environment you're in (client or server)

you can do it like this:

Check with a directive on the HTML where you put your apex chart if you're in browser or server

the directive:

import {Directive, Inject, OnInit, PLATFORM_ID, TemplateRef, ViewContainerRef} from '@angular/core';
import {isPlatformServer} from '@angular/common';

@Directive({
    selector: '[SSR]'
})
export class SSRDirective implements OnInit {

    constructor(
        private viewContainer: ViewContainerRef,
        private templateRef: TemplateRef<any>,
        @Inject(PLATFORM_ID) private platformId
    ) { }

    ngOnInit() {
        if (isPlatformServer(this.platformId)) {
            this.viewContainer.clear();
        } else {
            this.viewContainer.createEmbeddedView(this.templateRef);
        }

    }
}

And you can use like this:

<apextag   [bidings]
 *SSR> 
            </apextag>

Upvotes: 1

Related Questions