Note: This tutorial is for prerendering an application after following these instructions.
- Assuming we want lazy loading...
npm i @nguniversal/module-map-ngfactory-loader --save
Then update your app.module.ts
import { BrowserTransferStateModule } from '@angular/platform-browser';
@NgModule({
imports: [
// ...
BrowserModule.withServerTransition({ appId: 'serverApp' }),
BrowserTransferStateModule
]
})
- Update
app.server.module.ts
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
@NgModule({
imports: [
// ...
ServerTransferStateModule,
ModuleMapLoaderModule
],
})
- Before we implement the prerendering stuff, here are some packages we need
npm i -D webpack-cli fs-extra http-server ts-loader
- Create a
server.tsconfig.json
file and place it at the root of your app.
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
},
"include": ["prerender.ts"]
}
- Add a
prerender.ts
file to your app root as well. Be sure to update theROUTES
variable if you have any additional routes you also want to prerender.
// Load zone.js for the server.
import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import {readFileSync, writeFileSync, existsSync, mkdirSync} from 'fs';
import {join} from 'path';
import {enableProdMode} from '@angular/core';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
// Import module map for lazy loading
import {provideModuleMap} from '@nguniversal/module-map-ngfactory-loader';
import {renderModuleFactory} from '@angular/platform-server';
const ROUTES = [
'/'
];
const minify = require('html-minifier').minify;
const domino = require('domino');
const template = readFileSync(join('browser', 'index.html'), 'utf8');
const win = domino.createWindow(template);
global['window'] = win;
global['document'] = win.document;
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./server/main');
/// New function implementation ///
async function prerender() {
const BROWSER_FOLDER = join(process.cwd(), 'browser');
// Get the app index
const index = readFileSync(join('browser', 'index.html'), 'utf8');
const domino = require('domino');
const win = domino.createWindow(index);
global['document'] = win.document;
// Loop over each route
for (const route of ROUTES) {
const fullPath = join(BROWSER_FOLDER, route);
// Make sure the directory structure is there
if (!existsSync(fullPath)) {
mkdirSync(fullPath);
}
// Render with Universal
const html = await renderModuleFactory(AppServerModuleNgFactory, {
document: index,
url: route,
extraProviders: [provideModuleMap(LAZY_MODULE_MAP)]
});
await writeFileSync(join(fullPath, minify('index.html')), html);
}
process.exit();
}
prerender();
- Update your
package.json
to represent the following.
"compile:server": "tsc -p server.tsconfig.json",
"generate:prerender": "cd dist && node prerender",
"build:prerender": "npm run build:client-and-server-bundles && npm run compile:server && npm run generate:prerender",
"serve:prerender": "http-server ./dist/browser"
npm run build:prerender && npm run serve:prerender
In order to make these instructions work, the following changes should be made.
-
Install hamerjs package and also hammerjs type
npm install hammerjs --save && npm install @types/hammerjs --save
-
Change
declare var Hammer: any;
toimport * as Hammer from "hammerjs";
-
Change
Hammer.DIRECTION_All
toHammer.DIRECTION_ALL
-
Change
inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput,
toinputClass: Hammer.TouchInput,