Skip to content

Commit e287e8b

Browse files
docs: Update Universal Story to include module map
@nguniversal/module-map-ngfactory-loader has been release and with it, support for Lazy loading! This PR updates the existing story to guide users how they can use this functionality.
1 parent 3af138f commit e287e8b

File tree

1 file changed

+110
-14
lines changed

1 file changed

+110
-14
lines changed

docs/documentation/stories/universal-rendering.md

+110-14
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,17 @@ or
1616
$ yarn add @angular/platform-server --dev
1717
```
1818

19+
## Step 1: Install `@nguniversal/express-engine` and `@nguniversal/express-engine`
1920

20-
## Step 1: Prepare your app for Universal rendering
21+
```bash
22+
$ npm install --save @nguniversal/express-engine @nguniversal/module-map-ngfactory-loader
23+
```
24+
or
25+
```bash
26+
$ yarn add @nguniversal/express-engine @nguniversal/module-map-ngfactory-loader
27+
```
28+
29+
## Step 2: Prepare your app for Universal rendering
2130

2231
The first thing you need to do is make your `AppModule` compatible with Universal by addding `.withServerTransition()` and an application ID to your `BrowserModule` import:
2332

@@ -49,6 +58,7 @@ This example places it alongside `app.module.ts` in a file named `app.server.mod
4958
```javascript
5059
import {NgModule} from '@angular/core';
5160
import {ServerModule} from '@angular/platform-server';
61+
import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader';
5262

5363
import {AppModule} from './app.module';
5464
import {AppComponent} from './app.component';
@@ -59,6 +69,7 @@ import {AppComponent} from './app.component';
5969
// by the ServerModule from @angular/platform-server.
6070
AppModule,
6171
ServerModule,
72+
ModuleMapLoaderModule
6273
],
6374
// Since the bootstrapped component is not inherited from your
6475
// imported AppModule, it needs to be repeated here.
@@ -67,13 +78,20 @@ import {AppComponent} from './app.component';
6778
export class AppServerModule {}
6879
```
6980

70-
## Step 2: Create a server main file and tsconfig to build it
81+
## Step 3: Create a server main file and tsconfig to build it
7182

72-
Create a main file for your Universal bundle. This file only needs to export your `AppServerModule`. It can go in `src`. This example calls this file `main.server.ts`:
83+
Create a main file for your Universal bundle. This file exports your `AppServerModule` and enables production mode if build target is set to production. It can go in `src`. This example calls this file `main.server.ts`:
7384

7485
### src/main.server.ts:
7586

7687
```javascript
88+
import { environment } from './environments/environment';
89+
import { enableProdMode } from '@angular/core';
90+
91+
if (environment.production) {
92+
enableProdMode();
93+
}
94+
7795
export {AppServerModule} from './app/app.server.module';
7896
```
7997

@@ -105,7 +123,7 @@ Add a section for `"angularCompilerOptions"` and set `"entryModule"` to your `Ap
105123
}
106124
```
107125

108-
## Step 3: Create a new project in `.angular-cli.json`
126+
## Step 4: Create a new project in `.angular-cli.json`
109127

110128
In `.angular-cli.json` there is an array under the key `"apps"`. Copy the configuration for your client application there, and paste it as a new entry in the array, with an additional key `"platform"` set to `"server"`.
111129

@@ -166,8 +184,8 @@ With these steps complete, you should be able to build a server bundle for your
166184
# This builds the client application in dist/
167185
$ ng build --prod
168186
...
169-
# This builds the server bundle in dist-server/
170-
$ ng build --prod --app 1
187+
# This builds the server bundle in dist-server/ and disables output hashing
188+
$ ng build --prod --app 1 --output-hashing=false
171189
Date: 2017-07-24T22:42:09.739Z
172190
Hash: 9cac7d8e9434007fd8da
173191
Time: 4933ms
@@ -179,25 +197,103 @@ chunk {1} styles.d41d8cd98f00b204e980.bundle.css (styles) 0 bytes [entry] [rende
179197

180198
With this bundle built, you can use `renderModuleFactory` from `@angular/platform-server` to test it out.
181199

200+
### prerender.js:
201+
182202
```javascript
183203
// Load zone.js for the server.
184204
require('zone.js/dist/zone-node');
205+
require('reflect-metadata')
206+
const fs = require('fs');
185207

186208
// Import renderModuleFactory from @angular/platform-server.
187-
var renderModuleFactory = require('@angular/platform-server').renderModuleFactory;
209+
const { renderModuleFactory } = require('@angular/platform-server');
210+
211+
// Import module map for lazy loading
212+
const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader');
213+
214+
// Import the AOT compiled factory for your AppServerModule.
215+
// This import will change with the hash of your built server bundle.
216+
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require(`./dist-server/main.bundle`);
217+
218+
// Load the index.html file containing referances to your application bundle.
219+
const index = fs.readFileSync('./dist/index.html', 'utf8');
220+
221+
// Writes rendered HTML to ./dist/index.html, replacing the file if it already exists.
222+
renderModuleFactory(AppServerModuleNgFactory, {
223+
document: index,
224+
url: '/',
225+
extraProviders: [
226+
provideModuleMap(LAZY_MODULE_MAP)
227+
]
228+
})
229+
.then(html => fs.writeFileSync('./dist/index.html', html));
230+
```
231+
232+
After pre rendering the application with `renderModuleFactory` we can now serve the application, to do this create a server.js file within the dist folder
233+
234+
### server.js:
235+
236+
```javascript
237+
require('zone.js/dist/zone-node');
238+
require('reflect-metadata');
239+
const express = require('express');
240+
const fs = require('fs');
241+
242+
const { platformServer, renderModuleFactory } = require('@angular/platform-server');
243+
const { ngExpressEngine } = require('@nguniversal/express-engine');
244+
// Import module map for lazy loading
245+
const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader');
188246

189247
// Import the AOT compiled factory for your AppServerModule.
190248
// This import will change with the hash of your built server bundle.
191-
var AppServerModuleNgFactory = require('./dist-server/main.988d7a161bd984b7eb54.bundle').AppServerModuleNgFactory;
249+
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require(`./dist-server/main.bundle`);
250+
251+
const app = express();
252+
const port = 8000;
253+
const baseUrl = `http://localhost:${port}`;
254+
255+
// Set the engine
256+
app.engine('html', ngExpressEngine({
257+
bootstrap: AppServerModuleNgFactory,
258+
providers: [
259+
provideModuleMap(LAZY_MODULE_MAP)
260+
]
261+
}));
262+
263+
app.set('view engine', 'html');
264+
265+
app.set('views', './dist');
266+
app.use('/', express.static('./dist', {index: false}));
267+
268+
app.get('*', (req, res) => {
269+
res.render('index', {
270+
req,
271+
res
272+
});
273+
});
274+
275+
app.listen(port, () => {
276+
console.log(`Listening at ${baseUrl}`);
277+
});
278+
```
192279

193-
// Load the index.html file.
194-
var index = require('fs').readFileSync('./src/index.html', 'utf8');
280+
Test it out by running the server
281+
```bash
282+
$ node server.js
283+
```
195284

196-
// Render to HTML and log it to the console.
197-
renderModuleFactory(AppServerModuleNgFactory, {document: index, url: '/'}).then(html => console.log(html));
285+
An easy way to build and serve this application is to add the following script to package.json
286+
287+
DOS
288+
```bash
289+
$ "build:ssr": "ng build --prod && ng build --prod --app 1 --output-hashing=false && node prerender && node server.js",
290+
```
291+
or
292+
BASH
293+
```bash
294+
$ "build:ssr": "ng build --prod && ng build --prod --app 1 --output-hashing=false && node prerender && node server.js",
198295
```
199296

200297
## Caveats
201298

202-
* Lazy loading is not yet supported, but coming very soon. Currently lazy loaded routes aren't available for prerendering, and you will get a `System is not defined` error.
203-
* The bundle produced has a hash in the filename from webpack. When deploying this to a production server, you will need to ensure the correct bundle is required, either by renaming the file or passing the bundle name as an argument to your server.
299+
* The bundle produced has a hash in the filename from webpack. When deploying this to a production server, you will need to ensure the correct bundle is required, either by renaming the file or passing the bundle name as an argument to your server.

0 commit comments

Comments
 (0)