all content gets rendered after the page has loaded
javascript code then gets executed
main.ts loads the app.module which renders the application
the html sent to client is shown below
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Course Project</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
<script type="text/javascript" src="inline.bundle.js"></script>
<script type="text/javascript" src="polyfills.bundle.js"></script>
<script type="text/javascript" src="styles.bundle.js"></script>
<script type="text/javascript" src="vendor.bundle.js"></script>
<script type="text/javascript" src="main.bundle.js"></script>
</body>
</html>
not favorable for SEO because page has no content for search engine web crawlers
can use server-side rendering for at least the page's initial view as determine
by the URL
if initial request is for http://someServer:somePort/somePage the components which
compose somepage will be rendered server side
doing this permits search engine web crawlers to view content
Top
Index
univeral rendering means run in the browser but rendered on the server
caveats
- currently universal rendering does not support lazily loaded pages
-
server-side rendering cannot be done if a component manipulates the DOM
if DOM manipulation is needed use HostBinding and HostListening (see DropdownDirective)
never directly access or manipulate the DOM, don't use jQuery in an Angular app
how to set a project up for universal rendering can be found in the
Angular Wiki
first install the required packages
npm install --save @angular/platform-server @nguniversal/module-map-ngfactory-loader ts-loader
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
REM This builds the client application in dist/browser/
ng build --prod
...
REM This builds the server bundle in dist/server/
ng build --prod --app 1 --output-hashing=false
outputs
Date: 2017-07-24T22:42:09.739Z
Hash: 9cac7d8e9434007fd8da
Time: 4933ms
chunk {0} main.bundle.js (main) 9.49 kB [entry] [rendered]
chunk {1} styles.bundle.css (styles) 0 bytes [entry] [rendered]
Top
Index
creating the server main file
in app.module's imports property add withServerTransition method to the BrowserModule
import
the property is an identifier unique to the project
allow Angular to differentiate what was rendered on the server from what is rendered
on the client
...
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule.withServerTransition({ appId: 'my-universal-app'}),
...
],
bootstrap: [AppComponent]
})
export class AppModule { }
in the app folder add a new file
app.server.module.ts as shown below
the AppServerModule should import your AppModule followed by the ServerModule from
@angular/platform-server
the ModuleMapLoaderModule is needed to have lazy-loaded routes work
the bootstrapped component is not inherited from the imported AppModule, it needs
to be repeated here
module used for server-side rendering
import {NgModule} from '@angular/core';
import {ServerModule} from '@angular/platform-server';
import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader';
import {AppModule} from './app.module';
import {AppComponent} from './app.component';
@NgModule({
imports: [
AppModule,
ServerModule,
ModuleMapLoaderModule
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
in the src folder add a new file named main.server.ts
import { enableProdMode } from '@angular/core';
export { AppServerModule } from './app/app.server.module';
enableProdMode();
Top
Index
working on tsconfig configurations
in the video the changes described to tsconfig.json and the new tsconfig.app.json
were made by the CLI when the project was created
in the src folder create a file named
tsconfig.server.json
copy content of tsconfig.app.json and paste into tsconfig.server.json
under compiler options change the module property's value to
commonjs
add the angularCompilerOptions property and set the child entry module property
to the
<path>#<class name>
this tells angular where to look for server-side rendering
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "commonjs",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
],
"angularCompilerOptions": {
"entryModule": "app/aap.server.module#AppServerModule"
}
}
Top
Index
handling SSR (server-side rendering) as a new app (in .angular-cli.json)
in angular-cli.json and a second element to the
apps property of the json
data
the element is for the app which actually does the SSR
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "course-project"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"styles.css",
"../node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
},
{ "name": "universal", "platform": "server", "root": "src", "outDir": "dist-server",
"assets": [ "assets", "favicon.ico" ], "index": "index.html", "main": "main.server.ts",
"test": "test.ts", "tsconfig": "tsconfig.server.json", "testTsconfig": "tsconfig.spec.json",
"prefix": "app", "styles": [ "styles.css", "../node_modules/bootstrap/dist/css/bootstrap.min.css"
], "scripts": [], "environmentSource": "environments/environment.ts", "environments":
{ "dev": "environments/environment.ts", "prod": "environments/environment.prod.ts"
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}
command to build the client
ng build --prod
command to buid the server
ng build --prod --app 1 --output-hashing=false
an alternative to these commands is to add a script to build them both at the same
time
add the script package.json
{
"name": "course-project",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"build:ssr": "ng build -- prod && ng build --prod --app 1 --output-hashing=none",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
...
},
"devDependencies": {
...
}
}
to execute the script use the command
npm run build:ssr
Top
Index
build a node.js server, node express server
permits running server-side Javascript unlike most servers
run the following commands
npm install --save express
and
npm install --save @nguniversal/express-engine
and
npm install --save @nguniversal/module-map-ngfactory-loader
create new file in root named
server.js
'use strict';
require('zone.js/dist/zone-node');
require('reflect-metadata');
const express = require('express');
const ngUniversal = require('@nguniversal/express-engine');
const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader');
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist-server/main.bundle');
function angularRouter(req, res) {
res.render('index', {req, res});
}
const app = express();
app.engine('html', ngUniversal.ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
app.set('view engine', 'html');
app.set('views', 'dist');
app.get('/', angularRouter);
app.use(express.static(`${__dirname}/dist`));
app.get('*', angularRouter);
app.listen(3000, () => {
console.log('Listening on port 3000');
});
to run the server use the command
node server.js
Top
Index
to deploy the project these are needed
- server.js
- package.json
- dist folder
- dist-server folder
Top
Index