Table of Contents
Why Do We Need Server-side Rendering?
Getting started with Angular Universal
Step 2 - Building and Serving the App
"window is not defined" And "Document is not defined"
Solution for "window is not defined":
Why Do We Need Server-side Rendering?
Angular applications are client-side applications that execute on the browser - which means they are rendered on the client, not on the server. You can add server-side rendering to your app using Angular Universal.
There are two main reasons to create a server-side version of your application:
- Performance: Rendering Angular on the server-side improves the performance of your application, particularly on mobile and low-powered devices since the browser will not need extra time to render content which reduces the time for the First-contentful Paint
- SEO: Server rendering allows search engine crawlers to easily crawl your web app which helps with Search Engine Optimization
What is Angular Universal?
Angular Universal is a technology that takes care of rendering Angular applications on the server. It runs on the server-side and generates pages that are sent to the client browser which allows the application to render more quickly, giving users a chance to view the application layout before it becomes fully interactive.
Getting started with Angular Universal
Step 1 - Add @nguniversal
From your app directory open a terminal and run the following command:
ng add @nguniversal/express-engine --clientProject YourProjectName
This command will install the required packages and it will add and update all required files to implement server-side rendering in angular application.
CREATE src/main.server.ts
CREATE src/app/app.server.module.ts
CREATE src/tsconfig.server.json
CREATE server.ts
UPDATE package.json
UPDATE angular.json
UPDATE src/main.ts
UPDATE src/app/app.module.ts
UPDATE tsconfig.json
Step 2 - Building and Serving the App
run the following commands:
npm run build:ssr
npm run serve:ssr
This will serve the angular application with server-side rendered pages on http://localhost:4000 address
npm run build:ssr : This command compiles and create dist folder and in that folder there were 2 folders /browser and /server, normal build and server build.
npm run serve:ssr : This command serve /server folder only, but when any request is made from browser, it serve template and other static files from /browser folder.
Things to remember:
Since a Universal application runs on the server and not in a browser, there are a few things you need to watch out for in your application code:
- Check your use of browser-specific objects, such as window, document, or location. These don’t exist on the server.
- By importing the functions isPlatformBrowser and isPlatformServerfrom @angular/common, injecting the PLATFORM_ID token into your component, and running the imported functions to see whether you’re on the server or the browser.
Example:
import { OnInit, PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformServer } from '@angular/common';
export class SomeClass implements OnInit {
constructor(@Inject(PLATFORM_ID) private platformId: Object) { }
ngOnInit() {
if (isPlatformServer(this.platformId)) {
// do server side stuff
}
}
}
"window is not defined" And "Document is not defined"
One of the most common issues when using Angular Universal is the lack of browser global variables in the server environment.
Solution for "window is not defined":
Create service file (Example: global-object.service.ts)
For Example:
import { Injectable, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
@Injectable()
export class GlobalObjectService {
constructor(@Inject(DOCUMENT) private doc: Document) {}
getWindow(): Window | null {
return this.doc.defaultView;
}
getLocation(): Location {
return this.doc.location;
}
createElement(tag: string): HTMLElement {
return this.doc.createElement(tag);
}
}
After creating service file use this file wherever window object is used
import { GlobalObjectService } from '@shared/services';
import { isPlatformBrowser } from '@angular/common';
export class AppComponent implements OnInit {
constructor(
windowRef: GlobalObjectService,
@Inject(PLATFORM_ID) private platformId: object,
) {
this.windowRef = windowRef.getWindow();
}
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
this.windowRef.scrollTo(0, 0);
}
}
}
This code is solve "Window is not defined" error
Solution for "Document is not defined":
The above solution is also use for "document is not defined" error but if you still facing this error you can also use this code in server.ts file
const domino = require('domino');
const fs = require('fs');
const path = require('path');
const templateA = fs
.readFileSync(path.join('dist/NinsUserFrontend/browser', 'index.html'))
.toString();
const win = domino.createWindow(templateA);
win.Object = Object;
win.Math = Math;
global['window'] = win;
global['document'] = win.document;
global['branch'] = null;
global['object'] = win.object;
Conclusion:
We've seen that Server-side rendering works by rendering a part of your client-side app on the server instead of the client.
This can help increase performance and allow all search engine crawlers and social media networks to easily crawl your application for SEO purposes.
We also used the @nguniversal/express-engine to quickly create a Universal version of our Angular application and prepare our app for server-side rendering.
Reference:
https://angular.io/guide/universal
https://github.com/angular/universal/blob/master/docs/gotchas.md