Angular Cli now supports this in version 1.3.0-rc.0 and up.
You can install this version using
npm install -g @angular/cli
Setup Instructions from Angular Cli Wiki on Universal Rendering
I have a demo app which can be found on GitHub
Source: https://github.com/joejordanbrown/angular-cli-universal
Live Demo: https://uixd.co.uk/open-source-software/angular-cli-universal/
Step 1: Create new Angular Cli App
$ ng new angular-cli-universal
Step 2: Install @angular/platform-server
Install @angular/platform-server into your project. Make sure you use the same version as the other @angular packages in your project.
$ npm install --save-dev @angular/platform-server
or
$ yarn add @angular/platform-server
Step 3: Prepare your app for Universal rendering
The first thing you need to do is make your AppModule compatible with Universal by adding .withServerTransition() and an application ID to your BrowserModule import:
src/app/app.module.ts:
@NgModule({
bootstrap: [AppComponent],
imports: [
BrowserModule.withServerTransition({appId: 'my-app'}),
...
],
})
export class AppModule {}
Next, create a module specifically for your application when running on the server. It's recommended to call this module AppServerModule.
This example places it alongside app.module.ts in a file named app.server.module.ts:
src/app/app.server.module.ts:
import {NgModule} from '@angular/core';
import {ServerModule} from '@angular/platform-server';
import {AppModule} from './app.module';
import {AppComponent} from './app.component';
@NgModule({
imports: [
AppModule,
ServerModule,
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
Step 4: Create a server main file and tsconfig to build it
Create the 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:
src/main.server.ts:
export {AppServerModule} from './app/app.server.module';
Copy tsconfig.app.json to tsconfig-server.json and change it to build with a "module" target of "commonjs".
Add a section for "angularCompilerOptions" and set "entryModule" to your AppServerModule, specified as a path to the import with a hash (#) containing the symbol name. In this example, this would be src/app/app.server.module#AppServerModule.
src/tsconfig.server.json:
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "commonjs",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
],
"angularCompilerOptions": {
"entryModule": "app/app.server.module#AppServerModule"
}
}
Step 5: Create a NodeJS server file
You need to create a NodeJS server to render and serve the app. This example uses express.
Install express and compression
$ npm install --save express compression @nguniversal/express-engine
or
$ yarn add express compression @nguniversal/express-engine
src/express.server.js:
const path = require('path');
const fs = require('fs');
const express = require('express');
const compression = require('compression');
const ngExpressEngine = require('@nguniversal/express-engine').ngExpressEngine;
require('zone.js/dist/zone-node');
require('rxjs/add/operator/filter');
require('rxjs/add/operator/map');
require('rxjs/add/operator/mergeMap');
var hash;
fs.readdirSync(__dirname).forEach(file => {
if (file.startsWith('main')) {
hash = file.split('.')[1];
}
});
const AppServerModuleNgFactory = require('./main.' + hash + '.bundle').AppServerModuleNgFactory;
const app = express();
const port = Number(process.env.PORT || 8080);
app.engine('html', ngExpressEngine({
baseUrl: 'http://localhost:' + port,
bootstrap: AppServerModuleNgFactory
}));
app.set('view engine', 'html');
app.set('views', path.join(__dirname, '/../browser'));
app.use(compression());
app.use('/', express.static(path.join(__dirname, '/../browser'), {index: false}));
app.get('/*', function (req, res) {
res.render('index', {
req: req,
});
});
app.listen(port, function() {
console.log(`Listening at ${port}`);
});
Step 6: Create a new project in .angular-cli.json
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".
Then, remove the "polyfills" key - those aren't needed on the server and adjust "main", and "tsconfig" to point to the files you wrote in step 2. Finally, adjust "outDir" to a new location (this example uses dist/server).
.angular-cli.json:
{
...
"apps": [
{
"outDir": "dist/browser",
},
{
"platform": "server",
"root": "src",
"outDir": "dist/server",
"assets": [
"assets",
"favicon.ico",
"express.server.js"
],
"index": "index.html",
"main": "main.server.ts",
"test": "test.ts",
"tsconfig": "tsconfig.server.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
...
}
Building the bundle
With these steps complete, you should be able to build a server bundle for your application, using the --app flag to tell the CLI to build the server bundle, referencing its index of 1 in the "apps" array in .angular-cli.json:
$ ng build --prod
...
$ ng build --prod --app 1
Date: 2017-07-24T22:42:09.739Z
Hash: 9cac7d8e9434007fd8da
Time: 4933ms
chunk {0} main.988d7a161bd984b7eb54.bundle.js (main) 9.49 kB [entry] [rendered]
chunk {1} styles.d41d8cd98f00b204e980.bundle.css (styles) 0 bytes [entry] [rendered]
Starting the express server
$ node dist/server/express.server.js
View the Angular Cli Wiki for more details
https://github.com/angular/angular-cli/wiki/stories-universal-rendering