10

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>
Vladimir Djukic
  • 2,042
  • 7
  • 29
  • 60

3 Answers3

10

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

J J B
  • 8,540
  • 1
  • 28
  • 42
  • Thanks mate, this actually worked for me.... finally... after long struggle.... missing npm install @nguniversal/express-engine --save in your post! ;) – DS_web_developer Aug 07 '17 at 14:59
  • That's good, It took me a bit of playing at first to get it to work with the express server. Since they don't actually provide that part which is a shame. Thanks, I'll add `@nguniversal/express-engine` to the install part of the server. – J J B Aug 07 '17 at 15:03
  • I have also created a minimal/skeleton Angular Universal application here. https://github.com/gopivignesh/angular-universal. This source code can be used as a good base to build a new angular universal project. – gvm Nov 19 '17 at 04:50
  • I have an error , when I build the project. "Local workspace file ('angular.json') could not be found." – Kamuran Sönecek May 15 '18 at 08:37
  • 1
    @KamuranSönecek that's to do with the new `@angular/cli` in v6 they renamed the config to angular.json. Have you run `ng update`? My answer here will help https://stackoverflow.com/questions/41403810/how-to-upgrade-angular-2-version-in-angular-cli-project/41403927#41403927 you should have run `ng update @angular/cli --migrate-only --from=1` – J J B May 15 '18 at 22:33
1

You can use universal-cli from https://github.com/devCrossNet/angular-cli

It is a fork from angular-cli but this work with angular universal.

After you intalled with npm install -g universal-cli create a new project with

ung new PROJECT_NAME --universal

Then the project should be ready to serve with

cd PROJECT_NAME ung serve

I have not tested with a existing angular-cli project but maybe ung init --universal could help

  • angular universal is quite immature. i am also waiting for stable release. – Vinay Pandya Feb 25 '17 at 07:01
  • @VinayPandya , did you find any way to use angular universal with cli project. Any input will be helpful. – Praveen Rana Mar 21 '17 at 11:50
  • yes there universal-cli project https://github.com/devCrossNet/universal-cli, which combines angular-cli and universal project, but main problem you can see in package.json, it is using angular 2.2.3. angular 2 latest version is 2.4.10. possible that you can face some bugs. – Vinay Pandya Mar 21 '17 at 12:42
-1

Now Angular-cli 1.3 has been released the documentation has been updated to cover Universal with a guide here. There is a guide and sample for getting it all working with Universal + material 2 and an Express server here.

JayChase
  • 11,174
  • 2
  • 43
  • 52