1

Noob(almost) here!

I have an Angular application which is based on "@angular/core": "^8.2.14", and it is CSR (Client-Side Rendering). Now I want to switch it to SSR (Server-side rendering).

I have done step by step of Angular documentation , but my application can't find window object, I have searched many websites for the solution, this is my final server.ts file below, but my app cant find window, and sometimes other packages like SVG!

// These are important and needed before anything else
import 'zone.js/dist/zone-node';
import 'reflect-metadata';

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

import * as express from 'express';
import {join} from 'path';

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist');

const domino = require('domino');
const fs = require('fs');
const path = require('path');
// index from browser build!
const template = fs.readFileSync(path.join('.', 'dist', 'browser', 'index.html')).toString();
// for mock global window by domino
const win = domino.createWindow(template);
// mock
global['window'] = win;
// not implemented property and functions
Object.defineProperty(win.document.body.style, 'transform', {
    value: () => {
        return {
            enumerable: true,
            configurable: true,
        };
    },
});
// mock documnet
global['document'] = win.document;
// othres mock
global['CSS'] = null;
// global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['Prism'] = null;

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/metronic-server/main');

// Express Engine
import {ngExpressEngine} from '@nguniversal/express-engine';
// Import module map for lazy loading
import {provideModuleMap} from '@nguniversal/module-map-ngfactory-loader';

app.engine('html', ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [
        provideModuleMap(LAZY_MODULE_MAP)
    ]
}));

app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));

// TODO: implement data requests securely
app.get('/api/*', (req, res) => {
    res.status(404).send('data requests are not supported');
});

// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));

// All regular routes use the Universal engine
app.get('*', (req, res) => {
    global['navigator'] = {userAgent: req['headers']['user-agent']} as Navigator;
    res.render('index', {req});
});

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

but I still have error: enter image description here

How can I fix this error?

Many thanks.

Amin
  • 413
  • 6
  • 12
  • After fixing this error, I will get SVG is not defined! You know I used packages which are not supported by Angular SSR, I mean is there any other way not to checking isPlatformBrowser support? – Amin Oct 29 '20 at 11:50

1 Answers1

3

Since Angular Universal is server side rendered, there is no window object server side. This means you either need to mock the window object away like here in your server.ts file:

const domino = require('domino');
const fs = require('fs');
const path = require('path');
const templateA = fs
.readFileSync(path.join('.', 'dist', 'index.html'))
.toString();
const win = domino.createWindow(templateA);
win.Object = Object;
win.Math = Math;

win.navigator.language = 'en';

global['Event'] = null;
global['window'] = win;
global['document'] = win.document;
global['branch'] = null;
global['object'] = win.object;
global['localStorage'] = localStorage;
global['sessionStorage'] = sessionstorage;
global['navigator'] = win.navigator ;

or wrap every occurance of window.* inside your Angular files with an

if (isPlatformBrowser(platformId)) {
  window.*
}

Refer to this answer

  • After fixing this error, I will get SVG is not defined! You know I used packages which are not supported by Angular SSR, I mean is there any other way not to checking isPlatformBrowser support? – Amin Oct 29 '20 at 11:51
  • Which package are you using that is causing this error? And what is the exact error message? – Daniel von Mirbach Nov 01 '20 at 07:29
  • I'm using apecxchart for angular, inside of it, it is using https://svgjs.com/docs/3.0/, but the problem is not limited to this package, because after solving this, another package might cause an error of undefined. – Amin Nov 05 '20 at 07:00