Reputation: 433
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}`);
});
How can I fix this error?
Many thanks.
Upvotes: 1
Views: 1123
Reputation: 102
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
Upvotes: 3