Anyone have experience with installing angular universal with angular cli projects?
I tried to follow this guide:
https://universal.angular.io/quickstart/
But after I do this:
typings install node express body-parser serve-static express-serve-static-core mime --global
I get error:
typings INFO globaldependencies "express" lists global dependencies on "node" that must be installed manually
typings ERR! message Unable to find "node" ("npm") in the registry.
typings ERR! message However, we found "node" for 2 other sources: "dt" and "env"
typings ERR! message You can install these using the "source" option.
typings ERR! message We could use your help adding these typings to the registry: https://github.com/typings/registry
typings ERR! caused by https://api.typings.org/entries/npm/node/versions/latest responded with 404, expected it to equal 200
typings ERR!
typings ERR! cwd /home/universal
typings ERR! system Linux 3.10.17
typings ERR! command "/usr/bin/node" "/usr/bin/typings" "install" "node" "express" "body-parser" "serve-static" "express-serve-static-core" "mime" "--global"
typings ERR! node -v v4.2.4
typings ERR! typings -v 2.0.0
typings ERR!
typings ERR! If you need help, you may report this error at:
typings ERR! <https://github.com/typings/typings/issues>
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: [
// Add .withServerTransition() to support Universal rendering.
// The application ID can be any identifier which is unique on
// the page.
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: [
// The AppServerModule should import your AppModule followed
// by the ServerModule from @angular/platform-server.
AppModule,
ServerModule,
],
// Since the bootstrapped component is not inherited from your
// imported AppModule, it needs to be repeated here.
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": "./",
// Set the module format to "commonjs":
"module": "commonjs",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
],
// Add "angularCompilerOptions" with the AppServerModule you wrote
// set as the "entryModule".
"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,
// res: res
});
});
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": [
{
// Keep your original application config the same apart from changing outDir to dist/browser.
// It will be app 0.
"outDir": "dist/browser",
},
{
// This is your server app. It is app 1.
"platform": "server",
"root": "src",
// Build to dist/server instead of dist. This prevents
// client and server builds from overwriting each other.
"outDir": "dist/server",
"assets": [
"assets",
"favicon.ico",
"express.server.js"
],
"index": "index.html",
// Change the main file to point to your server main.
"main": "main.server.ts",
// Remove polyfills.
// "polyfills": "polyfills.ts",
"test": "test.ts",
// Change the tsconfig to point to your server config.
"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:
# This builds the client application in dist/browser/
$ ng build --prod
...
# This builds the server bundle in dist/server/
$ 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With