Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jicee13/f6a5b24a3b2e41cc985ffc7d1a785f84 to your computer and use it in GitHub Desktop.
Save jicee13/f6a5b24a3b2e41cc985ffc7d1a785f84 to your computer and use it in GitHub Desktop.
After creating an Angular Universal application, here is how you can also make it suitable for prerendering.

Angular Universal Prerendering

Note: This tutorial is for prerendering an application after following these instructions.

These steps can also be easily applied to any general Angular Univseral app as well.

  1. 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
  ]
})
  1. Update app.server.module.ts
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';


@NgModule({
  imports: [
    // ...
    ServerTransferStateModule,
    ModuleMapLoaderModule
  ],
})
  1. Before we implement the prerendering stuff, here are some packages we need

npm i -D webpack-cli fs-extra http-server ts-loader

  1. 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"]
  }
  1. Add a prerender.ts file to your app root as well. Be sure to update the ROUTES 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();
  1. 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"
  1. npm run build:prerender && npm run serve:prerender

Angular Universal Custom Hammerjs Configs

In order to make these instructions work, the following changes should be made.

  1. Install hamerjs package and also hammerjs type npm install hammerjs --save && npm install @types/hammerjs --save

  2. Change declare var Hammer: any; to import * as Hammer from "hammerjs";

  3. Change Hammer.DIRECTION_All to Hammer.DIRECTION_ALL

  4. Change inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput, to inputClass: Hammer.TouchInput,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment