Angular Universal SSR(Server-Side Rendering)

Angular Universal SSR(Server-Side Rendering)

Angular Universal SSR (Server-Side Rendering)

?

What is Angular Universal?

Angular Universal is a pre-rendering solution for Angular to be short cut.

In a normal Angular/Single Page application we bring data to the client and build HTML and render it on the browser.

But in some situations, we may need to render the data on the server ahead of time and then send the rendered HTML to the client. This is achieved by Angular Universal. It allows server-side rendering of the HTML.

?

Implementing the Angular Universal step by step using real life example.

Sure! Let's modify the example to use standalone components and lazy loading of routes. We'll also ensure the SSR configuration remains the same, using a configuration file to define the SSR routes.

?Step-by-Step Implementation

? Here example is shown for Standalone components. If you are using Angular version prior to 14.0 you should import the components in the app.modules.ts and in the route.ts you have to use loadchildren instead of loadcomponent for lazyloading.

1. Create a New Angular Project

ng new universalExample --routing

cd universalExample

Add Angular Universal to the Project

Add Angular Universal to your project using the Angular CLI:

ng add @nguniversal/express-engine

This command adds the necessary files and dependencies for Angular Universal, including server-specific files like server.ts.

Let us create an array of routes for the pages that should be server-side rendered (SSR). We will also set up a configuration file to define these routes, making it easy to manage in a real-life scenario with many pages.

?

1. Create Configuration File for SSR Routes

?

First, create a configuration file to define the SSR routes.

?

src/assets/config.json

?

```json

{

? "ssrRoutes": ["/", "/about", "/login", "/register"]

}

```

?

?2. Implement Standalone Components

?

Let's create standalone components for Home, About, Login, Register, and Dashboard.

?

** Run the following CLI commands to create the components

?

ng generate component home --standalone

ng generate component about --standalone

ng generate component login --standalone

ng generate component register --standalone

ng generate component dashboard --standalone

```

?

3. Implement the Components

?

Here are simple implementations for each standalone component:

?

src/app/home/home.component.ts

?

```typescript

import { Component } from '@angular/core';

import { CommonModule } from '@angular/common';

import { RouterModule } from '@angular/router';

?

@Component({

? selector: 'app-home',

? standalone: true,

? imports: [CommonModule, RouterModule],

? templateUrl: './home.component.html',

? styleUrls: ['./home.component.css']

})

export class HomeComponent {}

```

?

src/app/home/home.component.html

?

```html

<h2>Home</h2>

<p>Welcome to the home page. This page is server-side rendered.</p>

```

?

src/app/about/about.component.ts

?

```typescript

import { Component } from '@angular/core';

import { CommonModule } from '@angular/common';

import { RouterModule } from '@angular/router';

?

@Component({

? selector: 'app-about',

? standalone: true,

? imports: [CommonModule, RouterModule],

? templateUrl: './about.component.html',

? styleUrls: ['./about.component.css']

})

export class AboutComponent {}

```

?

src/app/about/about.component.html

?

```html

<h2>About</h2>

<p>This is the about page. This page is server-side rendered.</p>

```

?

src/app/login/login.component.ts

?

```typescript

import { Component } from '@angular/core';

import { CommonModule } from '@angular/common';

import { RouterModule, Router } from '@angular/router';

import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';

?

@Component({

? selector: 'app-login',

? standalone: true,

? imports: [CommonModule, RouterModule, ReactiveFormsModule],

? templateUrl: './login.component.html',

? styleUrls: ['./login.component.css']

})

export class LoginComponent {

? loginForm: FormGroup;

?

? constructor(private fb: FormBuilder, private router: Router) {

??? this.loginForm = this.fb.group({

????? username: ['', [Validators.required]],

????? password: ['', [Validators.required]]

??? });

? }

?

? onSubmit(): void {

??? if (this.loginForm.valid) {

????? // Simulate successful login

????? this.router.navigate(['/dashboard']);

??? }

? }

}

```

?

src/app/login/login.component.html

?

```html

<h2>Login</h2>

<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">

? <div>

??? <label for="username">Username</label>

??? <input id="username" formControlName="username" />

??? <div *ngIf="loginForm.get('username').invalid && loginForm.get('username').touched">

????? Username is required.

??? </div>

? </div>

? <div>

??? <label for="password">Password</label>

??? <input id="password" type="password" formControlName="password" />

??? <div *ngIf="loginForm.get('password').invalid && loginForm.get('password').touched">

????? Password is required.

??? </div>

? </div>

? <button type="submit" [disabled]="loginForm.invalid">Login</button>

</form>

```

?

src/app/register/register.component.ts

?

```typescript

import { Component } from '@angular/core';

import { CommonModule } from '@angular/common';

import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';

?

@Component({

? selector: 'app-register',

? standalone: true,

? imports: [CommonModule, ReactiveFormsModule],

? templateUrl: './register.component.html',

? styleUrls: ['./register.component.css']

})

export class RegisterComponent {

? registerForm: FormGroup;

?

? constructor(private fb: FormBuilder) {

??? this.registerForm = this.fb.group({

????? name: ['', [Validators.required, Validators.minLength(3)]],

????? email: ['', [Validators.required, Validators.email]],

????? password: ['', [Validators.required, Validators.minLength(6)]]

??? });

? }

?

? onSubmit(): void {

??? if (this.registerForm.valid) {

????? console.log('Form Submitted', this.registerForm.value);

??? }

? }

}

```

?

src/app/register/register.component.html

?

```html

<h2>Register</h2>

<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">

? <div>

??? <label for="name">Name</label>

??? <input id="name" formControlName="name" />

??? <div *ngIf="registerForm.get('name').invalid && registerForm.get('name').touched">

????? Name is required and must be at least 3 characters long.

??? </div>

? </div>

? <div>

??? <label for="email">Email</label>

??? <input id="email" formControlName="email" />

??? <div *ngIf="registerForm.get('email').invalid && registerForm.get('email').touched">

????? Valid email is required.

??? </div>

? </div>

? <div>

??? <label for="password">Password</label>

??? <input id="password" type="password" formControlName="password" />

??? <div *ngIf="registerForm.get('password').invalid && registerForm.get('password').touched">

????? Password is required and must be at least 6 characters long.

??? </div>

? </div>

? <button type="submit" [disabled]="registerForm.invalid">Register</button>

</form>

```

?

src/app/dashboard/dashboard.component.ts

?

```typescript

import { Component } from '@angular/core';

import { CommonModule } from '@angular/common';

?

@Component({

? selector: 'app-dashboard',

? standalone: true,

? imports: [CommonModule],

? templateUrl: './dashboard.component.html',

? styleUrls: ['./dashboard.component.css']

})

export class DashboardComponent {}

```

?

src/app/dashboard/dashboard.component.html

?

```html

<h2>Dashboard</h2>

<p>This is the dashboard page. This page is client-side rendered.</p>

```

?

#### 4. Update Routing for Lazy Loading

?

Update the routing module to use lazy loading for the standalone components.

?

src/app/app-routing.module.ts

?

```typescript

import { NgModule } from '@angular/core';

import { RouterModule, Routes } from '@angular/router';

?

const routes: Routes = [

? { path: '', loadComponent: () => import('./home/home.component').then(m => m.HomeComponent) },

? { path: 'about', loadComponent: () => import('./about/about.component').then(m => m.AboutComponent) },

? { path: 'login', loadComponent: () => import('./login/login.component').then(m => m.LoginComponent) },

? { path: 'register', loadComponent: () => import('./register/register.component').then(m => m.RegisterComponent) },

? { path: 'dashboard', loadComponent: () => import('./dashboard/dashboard.component').then(m => m.DashboardComponent) },

];

?

@NgModule({

? imports: [RouterModule.forRoot(routes)],

? exports: [RouterModule]

})

export class AppRoutingModule { }

```

?

src/app/app.component.html

?

```html

<nav>

? <a routerLink="/">Home</a>

? <a routerLink="/about">About</a>

? <a routerLink="/login">Login</a>

? <a routerLink="/register">Register</a>

? <a routerLink="/dashboard">Dashboard</a>

</nav>

<router-outlet></router-outlet>

```

?

#### 5. Update the App Module

?

Ensure the app module is set up correctly to support the standalone components and routing.

?

src/app/app.module.ts

?

```typescript

import { NgModule } from '@angular/core';

import { BrowserModule } from '@angular/platform-browser';

?

import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';

?

@NgModule({

? declarations: [

??? AppComponent

? ],

? imports: [

??? BrowserModule.withServerTransition({ appId: 'serverApp' }),

??? AppRoutingModule

? ],

? providers: [],

? bootstrap: [AppComponent]

})

export class AppModule { }

```

?

#### 6. Load SSR Routes Configuration in the Server File

?

server.ts

?

```typescript

import 'zone.js/node';

?

import { ngExpressEngine } from '@nguniversal/express-engine';

import * as express from 'express';

import { join } from 'path';

import { readFileSync } from 'fs';

?

import { AppServerModule } from './src/main.server';

?

// Load SSR routes from configuration file

const config = JSON.parse(readFileSync(join(process.cwd(), 'src/assets/config.json'), 'utf8'));

const ssrRoutes = config.ssrRoutes;

?

// The Express app is exported so that it can be used by serverless Functions.

export function app(): express.Express {

? const server = express();

? const distFolder = join(process.cwd(), 'dist/universalExample/browser');

? const indexHtml

?

?= 'index.html';

?

? // Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)

? server.engine('html', ngExpressEngine({

??? bootstrap: AppServerModule,

? }));

?

? server.set('view engine', 'html');

? server.set('views', distFolder);

?

? // Serve static files from /browser

? server.get('*.*', express.static(distFolder, {

??? maxAge: '1y'

? }));

?

? // SSR for routes in the configuration file

? server.get(ssrRoutes, (req, res) => {

??? res.render(indexHtml, { req });

? });

?

? // CSR for other routes

? server.get('*', (req, res) => {

??? res.sendFile(join(distFolder, indexHtml));

? });

?

? return server;

}

?

function run(): void {

? const port = process.env.PORT || 4000;

?

? // Start up the Node server

? const server = app();

? server.listen(port, () => {

??? console.log(`Node Express server listening on https://localhost:${port}`);

? });

}

?

declare const __non_webpack_require__: NodeRequire;

?

if (require.main === module) {

? run();

}

?

export * from './src/main.server';

```

?

#### 7. Build and Serve the Application

?

Build the application for both server and browser:

?

```bash

npm run build:ssr

npm run serve:ssr

```

?

### Running the Application

?

Navigate to https://localhost:4000 in your browser. You should see:

?

- The Home, About, Login, and Register pages rendered server-side (SSR).

Right click on each of these pages and click View Page Source. See the tag<app-root>. You can see the rendered html. This means html is rendered already on the server side.

- The Dashboard page rendered client-side (CSR).

?Right click on the page and select View Page Source. See the tag<app-root>. It will be empty. Means html will be rendered on the client side.

### Summary

?

In this guide, we configured an Angular Universal application to use standalone components and lazy loading of routes. We also used a configuration file to define the SSR routes. This involved:

?

1)??????? Create new Angular Project.

2)??????? Install Angular Universal

3)??????? Creating standalone components for the application

4)??????? Setting up lazy loading for the routes.

5)??????? Creating a configuration file to define SSR routes.

6)??????? Configuring the server to load SSR routes from the configuration file.

7)??????? Building and serving the application with SSR.

?

By configuring the server to render specific routes using Angular Universal and loading these routes from a configuration file, we can easily manage SSR for multiple pages.

要查看或添加评论,请登录

Gopalkrishna Hegde的更多文章

  • FOUNDATION MODELS AND LLM COMPARISON

    FOUNDATION MODELS AND LLM COMPARISON

    GEN AI MODELS Foundation models and Large Language Models are used in the context of Generative AI(Gen AI). Although…

    2 条评论
  • Static Site Generation (SSG)

    Static Site Generation (SSG)

    SSG (Static Site Generation) using Angular What is static site Generation (SSG)? In Static Site Generation the page…

  • Demystifying Common Git Errors

    Demystifying Common Git Errors

    Introduction: In the world of software development, Git has become the cornerstone of version control. It empowers…

  • Angular Architecture

    Angular Architecture

    Architecture Angular is a popular open-source framework for building web and mobile applications. It follows the…

  • SetValue v/s PatchValue

    SetValue v/s PatchValue

    We will learn about how set the model values in Reactive Forms. It is done using SetValue and PatchValue provided by…

  • Features of ASP.NET CORE

    Features of ASP.NET CORE

    Following are some of the features of ASP.NET CORE It is open source.

社区洞察

其他会员也浏览了