Professional Documents
Culture Documents
ilovepdf_merged
ilovepdf_merged
ilovepdf_merged
Angular Universal
• Setting up an Angular application for SSR
• Creating a server-side rendered Angular
application
• ClientHydration (Examples) –session 10
• Implementing server-side rendering using
Angular Universal
• Task – In the SSR rendered page of your
application, include a new style for a button
element as client hydration. Note: Use
AfterInitView, @ViewChild
Setting up an Angular application for
SSR
• Enable Server Side Rendering
• To create a new project with SSR, run:
• ng new <projectname> --ssr
• To add SSR to an existing project, use the Angular
CLI ng add command.
• ng add @angular/ssr
• These commands create and update application
code to enable SSR and adds extra files to the
project structure.
Setting up an Angular application for
SSR – Three server.ts files
• my-app
• |-- server.ts # application server
• └── src
• |-- app
• | └── app.config.server.ts # server application
configuration
• └── main.server.ts # main server application
bootstrapping
• To verify that the application is server-side rendered,
run it locally with ng serve. The initial HTML request
should contain application content.
Configure Server Side Rendering
• The server.ts file configures a Node.js Express server and Angular server-
side rendering.
• CommonEngine is used to render an Angular application.
• adev/src/content/examples/ssr/server.ts
• // All regular routes use the Angular engine
• server.get('*', (req, res, next) => {
• const {protocol, originalUrl, baseUrl, headers} = req;
• commonEngine
• .render({
• bootstrap,
• documentFilePath: indexHtml,
• url: `${protocol}://${headers.host}${originalUrl}`,
• publicPath: browserDistFolder,
• providers: [{provide: APP_BASE_HREF, useValue: req.baseUrl}],
• })
• .then((html) => res.send(html))
CommonEngine accepting Object
• The render method of CommonEngine accepts
an object with the following properties:
Default
Properties Details
Value
documentFilePath File path of the initial DOM to use to bootstrap the server application.
Express Features
• Angular CLI will scaffold an initial express
server implementation focused on server-side
rendering your Angular application.
• This server can be extended to support other
features such as API routes, redirects, static
assets, and more.
Ref: https://expressjs.com/
Hydration
• Hydration is the process that restores the server side
rendered application on the client.
• This includes things like reusing the server rendered
DOM structures, persisting the application state,
transferring application data that was retrieved already
by the server, and other processes.
• Hydration is enabled by default when you use SSR.
• Without hydration enabled, server-side rendered
Angular applications will destroy and re-render the
application's DOM, which may result in a visible UI
flicker.
Client Hydration
• In SSR, the server generates HTML content for
initial page load, which is sent to the client.
• Once the client receives this HTML content, it can
rehydrate the page by attaching event listeners,
initializing state, and making it interactive.
• This process is known as client hydration.
• While Angular does not have a built-in method
named provideClientHydration(), developers
might use custom methods or techniques to
facilitate client-side hydration in SSR scenarios.
Custom Methods – Client Hydration
• Lifecycle Hooks: Angular provides lifecycle hooks such as ngAfterViewInit()
or ngOnInit() which can be utilized to trigger actions after the view has
been initialized on the client-side.
• Custom Services: Developers might create custom services or utilities to
handle client-side hydration logic, encapsulating tasks such as attaching
event listeners or initializing component state.
• Dynamic Content Loading: Using techniques like lazy loading or dynamic
component loading, developers can load additional content or
functionality on the client-side after the initial page load, enhancing user
experience and interactivity.
• Platform-Specific Code: By using Angular's PLATFORM_ID token and
isPlatformBrowser() function, developers can conditionally execute client-
specific code to ensure compatibility with both server-side and client-side
environments.
• import { Component, AfterViewInit } from '@angular/core';
• @Component({
• selector: 'app-hydration',
• template: `
• <div #hydratedContent>
• <!-- Content to be hydrated -->
• </div>
• `
• })
• export class HydrationComponent implements AfterViewInit {
• constructor() { }
• ngAfterViewInit(): void {
• // Accessing the DOM element after the view has been initialized
• const hydratedContentElement: HTMLElement = this.hydratedContent.nativeElement;
• @Component({
• selector: 'app-hydration',
• template: `
• <button #buttonElement ">Click me</button>
• <!-- <button #buttonElement (click)="handleClick()">Click me</button> -->
• `
• })
• export class HydrationComponent implements AfterViewInit {
• constructor() { }
• ngAfterViewInit(): void {
• // Accessing the DOM element after the view has been initialized
• const buttonElement: HTMLButtonElement = this.buttonElement.nativeElement;
• // handleClick(): void {
• // console.log('Button clicked via Angular event handler!');
• //}
• }
To avoid strict type checking
• "compilerOptions": {
"strictPropertyInitialization": false, ... }
Goal of Client Hydration
• While the specific implementation details may
vary depending on the requirements of the
application, the overarching goal of client
hydration is to ensure that the application
behaves consistently and interactively across
different environments, including SSR and
client-side rendering (CSR).
Caching data when using http client
• HttpClient cached outgoing network requests when
running on the server.
• This information is serialized and transferred to the
browser as part of the initial HTML sent from the
server.
• In the browser, HttpClient checks whether it has data in
the cache and if so, reuses it instead of making a new
HTTP request during initial application rendering.
• HttpClient stops using the cache once an application
becomes stable while running in a browser.
Client Caching Options
• HttpClient caches all HEAD and GET requests by default.
You can configure the cache by
using withHttpTransferCacheOptions when providing
hydration.
• bootstrapApplication(AppComponent, {
• providers: [
• provideClientHydration(withHttpTransferCacheOptions({
• includePostRequests: true
• }))
• ]
• });
Authoring Sever Side Compatible
components
• Some common browser APIs and capabilities might not be
available on the server.
• Applications cannot make use of browser-specific global
objects like window, document, navigator, or location as
well as certain properties of HTMLElement.
• In general, code which relies on browser-specific symbols
should only be executed in the browser, not on the server.
• This can be enforced through
the afterRender and afterNextRender lifecycle hooks. These
are only executed on the browser and skipped on the
server.
Example Client Side Element
Rendering –in SSR
• import { Component } from '@angular/core';
• @Component({ selector: 'app-example',
template: ` <div *ngIf="isBrowser"> This
content is visible only in the browser. </div> `
})
• export class ExampleComponent {
• isBrowser: boolean; constructor() {
this.isBrowser = typeof window !==
'undefined'; } }
Using Angular Service Worker
• you are using Angular on the server in
combination with the Angular service worker,
the behavior deviates from the normal server-
side rendering behavior.
• The initial server request will be rendered on
the server as expected.
• However, after that initial request, subsequent
requests are handled by the service worker
and always client-side rendered.
Creating angular universal client
application
• We need universal client application
workspace to develop universal applications
• ng generate universal –client-project
*projectname*
Implementing server-side rendering
using Angular Universal
• A normal Angular application executes in
the browser, rendering pages in the DOM in
response to user actions.
• Angular Universal executes on the server,
generating static application pages that later get
bootstrapped on the client.
• This means that the application generally renders
more quickly, giving users a chance to view the
application layout before it becomes fully
interactive.
Angular Universal Server Module
• he Angular CLI compiles and bundles the
Universal version of the app with the Ahead-
of-Time (AOT) compiler.
• A Node.js Express web server compiles HTML
pages with Universal based on client requests.
• To create the server-side app
module, app.server.module.ts, run the
following CLI command.
• ng add @nguniversal/express-engine
Angular Universal Server Module
• The command creates the following folder structure.
• src/
• index.html app web page
• main.ts bootstrapper for client app
• main.server.ts * bootstrapper for server app
• style.css styles for the app
• app/ ... application code
• app.server.module.ts * server-side application module
• server.ts * express web server
• tsconfig.json TypeScript solution style configuration
• tsconfig.base.json TypeScript base configuration
• tsconfig.app.json TypeScript browser application configuration
• tsconfig.server.json TypeScript server application configuration
• tsconfig.spec.json TypeScript tests configuration
Render Application using Universal
• To start rendering your app with Universal on
your local system, use the following
command.
• npm run dev:ssr
• Open a browser and navigate
to http://localhost:4200/
renderModule() in Express
• Universal applications use the Angular platform-server package (as
opposed to platform-browser), which provides server implementations of
the DOM, XMLHttpRequest, and other low-level features that don't rely on
a browser.
• The server (Node.js Express in this guide's example) passes client requests
for application pages to the NgUniversal ngExpressEngine. Under the
hood, this calls Universal's renderModule() function, while providing
caching and other helpful utilities.
• The renderModule() function takes as inputs a template HTML page
(usually index.html), an Angular module containing components, and
a route that determines which components to display. The route comes
from the client's request to the server.
• Each request results in the appropriate view for the requested route.
The renderModule() function renders the view within the <app> tag of the
template, creating a finished HTML page for the client.
• Finally, the server returns the rendered page to the client.
Challenges with SSR
• If you throttle your network speed so that the client-
side scripts take longer to download (instructions
below), you'll notice:
• Clicking a hero on the Heroes page does nothing.
• You can't add or delete a hero.
• The search box on the Dashboard page is ignored.
• The Back and Save buttons on the Details page don't
work.
• User events other than routerLink clicks aren't
supported. You must wait for the full client app to
bootstrap and run,
Throttle the network Speed
• Simulate a slower network to see the
transition more clearly as follows:
• Open the Chrome Dev Tools and go to the
Network tab. (ctrl + shft+I)
• Find the Network Throttling dropdown on the
far right of the menu bar.
• Try one of the "3G" speeds.
Session 11
Topics
• Issues to be resolved in Angular Universal
application creation
• Issues to be resolved in server side rendering
using Angular universal
• Architecture of angular universal
Creating angular universal client
application
• We need universal client application workspace to
develop universal applications
• ng generate universal –client-project *projectname*
• We faced the issue in creating a new angular
application with this command,
• Error: This command is not available when running the
Angular CLI outside a workspace.
• Inside smallservices application workspace:
• D:\softwares\smallservices>ng generate universal --
client-project
• Error: Unknown argument: client-project
To resolve the Issue
• Create a new Angular application: Once you have
Angular CLI installed, you can create a new
Angular application by running:
• ng new my-universal-app
• Install Angular Universal: Angular Universal is a
pre-rendering solution for Angular applications.
To add Angular Universal to your project, navigate
to your project directory and run:
• ng add @nguniversal/express-engine
New Angular Application creation
• ng add @nguniversal/express-engine
• This command will install the necessary packages and set up your
project for Angular Universal.
• Issue - NOT SUPPORTED: keyword "id", use "$id" for schema ID
• f you still encounter the same issue, try manually installing the
required packages for Angular Universal:
• npm install --save @nguniversal/express-engine
@nguniversal/module-map-ngfactory-loader
• @Injectable({
• providedIn: 'root',
• })
• export class HeroService {
• constructor() { }
• }
@Injectable() services
• src/app/heroes/heroes.component.ts
• src/app/heroes/heroes.component.ts
• src/app/heroes/heroes.component.ts
ngOnInit(): void { this.getHeroes(); }
Session 12
Topics
• Services
• Angular Universal Installation, Implementation
challenges
• Task: Creating services for various datasources
and accessing in components
Services Example 1
• In created service
• Dataservice.service.ts
accessdata1:string[];
• constructor(private dataservice1:DataserviceService)
• {}
ngAfterViewInit():void{
• this.getdata();
•
}
•
getdata():void{
• this.accessdata1=this.dataservice1.getdata();
• console.log("clienthydration" + this.accessdata1);
• }
•
}
•
Services Example 2
• Create a service to access data using a file
• ng generate service filedataservice
• import { Injectable } from '@angular/core';
• import { HttpClient } from '@angular/common/http';
• import { Observable } from 'rxjs';
•
• @Injectable({
• providedIn: 'root'
• })
• export class FiledataService {
• private apiUrl = 'assets/data.json'; // Path to your JSON file
•
• constructor(private http: HttpClient) { }
•
• fetchData(): Observable<any[]> {
• return this.http.get<any[]>(this.apiUrl);
• }
• }
Services Example 2
• Accessin the service in another component
• import { Component, OnInit } from '@angular/core';
• import { FiledataService } from './filedata.service';
•
• @Component({
• selector: 'app-example',
• templateUrl: './example.component.html',
• styleUrls: ['./example.component.css']
• })
• export class ExampleComponent {
• data: any[];
•
• constructor(private dataService: FiledataService) { }
•
• ngAfterViewInit(): void {
• this.fetchData();
• }
•
• fetchData(): void {
• this.dataService.fetchData().subscribe(data => {
• this.data = data;
• Console.log(this.data;
• });
• }
• }
Web development architecture
Angular SSR Webdevelopment
Architecture
Angular Universal – Current State
• https://angular-university.io/course/angular-
universal-course
• In-depth guide on Angular server-side
rendering and static pre-rendering using the
Angular SSR package (previously known as
Angular Universal)
Session 13
Topics
• Lazy loading
• Creating applications with modules
Lazy Loading
• By default, NgModules are eagerly loaded.
• This means that as soon as the application
loads, so do all the NgModules, whether they
are immediately necessary or not.
• For large applications with lots of routes, lazy
loading is a design pattern
• It loads NgModules as needed.
• Lazy loading helps keep initial bundle sizes
smaller, which in turn helps decrease load
times.
Lazy Loading Basics
• Procedure used for configuring a lazy-loaded
route.
• Setting up a lazy-loaded feature module
requires two main steps:
• Create the feature module with the Angular
CLI, using the --route flag.
• Configure the routes.
Set up an application
• src/app/app-
routing.module.tscontent_copyconst routes:
Routes = [ { path: 'customers',
loadChildren: () =>
import('./customers/customers.module').then(m =>
m.CustomersModule) } ];
The lazy-loading syntax uses loadChildren followed
by a function that uses the browser's built-
in import('...') syntax for dynamic imports.
The import path is the relative path to the module.
Add another feature module
• <router-outlet></router-outlet>
Route configuration files
• src/app/app-routing.module.ts
• src/app/customers/customers.module.ts
• src/app/customers/customers-
routing.module.ts
• src/app/orders/orders-routing.module.ts
(excerpt)
Verify lazy loading
• …
Package.json
• It is used by the npm CLI (and yarn) to identify your project
and understand how to handle the project's dependencies.
• It's the package. json file that enables npm to start your
project, run scripts
• Install dependencies, publish to the NPM registry
• makes it possible to publish your project to the NPM
registry
• makes it easy for others to manage and install your package
• helps NPM to manage a module's dependencies easily
• makes your package reproducible and shareable with other
developers
Example in package.json
• "dependencies": {
• "@angular/animations": "^17.3.0",
• "@angular/common": "^17.3.0",
• "@angular/compiler": "^17.3.0",
• …
• }
• whenever a user installs your project from the
NPM registry, the dependencies property ensures
package managers can automatically find and
install the packages listed.
tsconfig.json
• The tsconfig. json file is a configuration file for
TypeScript projects.
• It contains a set of options that control how
the TypeScript compiler behaves when
it transpiles TypeScript code to JavaScript.
In a first.component.ts
• declare const loggedInUsername: string;
•
• const users = [
• { name: "Oby", age: 12 },
• { name: "Heera", age: 32 },
• ];
•
• const loggedInUser = users.find((u) => u.name ===
loggedInUsername);
• console.log(loggedInUser.age);
• 'loggedInUser' is possibly 'undefined'.
Example tsconfig.json
• "compilerOptions": {
• …
• “strictNullChecks”: true,
• …
• }
• will raise an error that you have not made a
guarantee that the loggedInUser exists before
trying to use it.
Routing files - lazyloading
• Module routing related files
• Ex: ngo module
• ngo-routing.module.ts – empty routes specified
in routes list
• ngo.module.ts – the routes list for ngo module is
imported in this
• app-routing.module.ts – actual route list for lazy
load modules specified using loadchildren instead
of components
• app.module.ts – imports the app-routing.module
Create feature
Module for an existing application
• Create a feature module :CutomerDashbaord
using the CLI by entering the following
command in the root project directory.
• ng generate module CustomerDashboard
• –generates a CustomerDashboard.module.ts
• - It imports NgModule and CommonModule
Create a component for the Customer
Module
• To add a component, enter the following command at
the command line where customer-dashboard is the
directory where the CLI generated the feature module
• CustomerDashboard is the name of the component:
• ng generate component customer-
dashboard/CustomerDashboard
• It imports the component in
CustomerDashboard.module.ts
• import the new component import {
CustomerDashboardComponent } from './customer-
dashboard/customer-dashboard.component';
NgModule,CommonModule,Customer
DashBoardComponent
• The structure of an NgModule is the same
whether it is a root module or a feature module.
• The first imports NgModule, which, like the root
module, lets you use the @NgModule decorator
• The second imports CommonModule, which
contributes many common directives such as ngIf
and ngFor.
• The third import is the component created for
the module: CustomerDashBoardComponent
@NgModule decorator
• The declarations array is available for you to add declarables, which
are components, directives, and pipes that belong exclusively to this
particular module
• @NgModule({
• declarations: [
• CustomerDashboardComponent
• ],
• imports: [
• CommonModule,
• FormsModule,
• ]
• })
To use ngModel in
CustomerDashbaord component
template
• In CustomerDashboard.module.ts
• import { FormsModule } from '@angular/forms';
• And in the imports array, add the following
• imports: [
• …
• FormsModule,
• ]
•
Components and Modules
• A component is responsible for rendering a view
and handling user interactions with the view.
• A module can contain services, pipes, and other
code that is used by the components that are a
part of the module.
• A component is defined using a class and a
template.
• A module is defined using the Angular NgModule
decorator.
Lazyloading application ngoservice
• Modules – ngos,customers
• ng new ngoservice --no-standalone
• ng generate module ngos --route ngos --
module app.module
Task
• Create a fileservice for storing and retrieving
json data in local storage. Use this service in
ngo module.
Session 15
Angular Ivy
• Ivy is Angular’s next-generation compilation and rendering pipeline,
here to improve the performance of Angular, as well as its
developer experience.
• To appreciate Angular Ivy, you need to understand how Angular as a
JavaScript library works.
• When you write code in an Angular component, you are likely
writing TypeScript that will end up being compiled by the Angular
compiler to JavaScript that will be sent out to the DOM.
• This compiler is such an essential part of what makes Angular what
it is today, and every couple of years the Angular team rewrites it.
• The most recent rewrite of the Angular compiler and the runtime
system is called Angular
Angular Rendering Engine
• Ivy is the code name for Angular’s next-
generation compilation and rendering
pipeline.
• Starting from the ninth version release of
Angular, the new compiler and runtime
instructions (Ivy rendering engine) are used by
default instead of the older compiler and
runtime, known as View Engine.
Why Was Ivy Created?
• The purpose of Ivy is to improve the
performance of Angular, as well as its developer
experience.
• Bundle Sizes Are Smaller
• Build Time Improvements, Faster AOT Compiler
• More Useful Error Messages
• Internationalization and Type Checking Updates
• Style Binding Updates
• Testing Became Faster
Bundle Sizes Are Smaller
• course: Course;
• ngOnInit() {
• this.course = this.route.snapshot.data['course'];
• ....
• // SEO metadata
• this.title.setTitle(this.course.description);
• this.meta.addTag({name: 'description', content: this.course.longDescription});
• }
• }
The code during deployment
How Title, Meta Services Work
• On the server, these services will simply
render the tags as plain strings, while on the
client they will update the title and meta tags
directly in the DOM at component startup
time.
• The Google Search Engine will be able to use a
client-side rendered title and description meta
tags,
• But this is not true for all search engines.
Why Angular Universal? Social Media
Crawlers
• The same way that search engines will crawl
our pages looking for titles and descriptions,
social media crawlers from platforms like
Twitter will also do something very similar.
• Whenever we post a link to social media, the
social media platform will crawl the content of
the page, and it might try to extract some
information about the page to make the post
look better.
Example - Twitter post improved by
the Twitter crawler:
Social Media benefits of server-side
rendering
• The tweet originally contained only the text
and the link, but the Twitter crawler managed
to extract a picture, a title, some text and it
built a summary card based on the page
content.
• In order for the Twitter crawler to be able to
build this card, we currently need the
application to be server-side rendered and fill
in some special meta tags
Integration with Social Media
Crawlers – with application
• In a similar way to what we did with the SEO
meta tags, we can also add other tags that will
be used by social media crawlers to help
configure what the page will look like on social
media.
• For example, let's make the course page look
better on Twitter, by configuring a Twitter
summary card:
Eample – Witter Card
• ngOnInit() {
• this.course = this.route.snapshot.data['course'];
• ....
• // SEO metadata
• this.title.setTitle(this.course.description);
• this.meta.addTag({name: 'description', content: this.course.longDescription});
• // Twitter metadata
• this.meta.addTag({name: 'twitter:card', content: 'summary'});
• this.meta.addTag({name: 'twitter:site', content: '@AngularUniv'});
• this.meta.addTag({name: 'twitter:title', content: this.course.description});
• this.meta.addTag({name: 'twitter:description', content: this.course.description});
• this.meta.addTag({name: 'twitter:text:description', content: this.course.description});
• this.meta.addTag({name: 'twitter:image', content:
'https://avatars3.githubusercontent.com/u/16628445?v=3&s=200'});
• }
• }
In Twitter – the page display
The twitter crawler now has all the necessary information for creating a summary card
for this page, producing a tweet for a course that looks like this:
Application Shell
• With our application up and running, we are now going to
implement a couple of performance optimizations that are usually
used together with server-side rendering.
• Imagine a page where there is a lot of data that is scrollable below
the fold: it might be better to conditionally render only some of the
data that the user sees above the fold on the server, and then take
care of the rest on the client after the application bootstraps.
• What we want to do is to send some HTML instead of a blank page,
but maybe not all the HTML initially.
• This initial HTML that we send to the user is known as
an Application Shell, and it might be as simple as a top menu bar
and a loading indicator, but it might be much more depending on
the page.
How to choose what gets rendered or
not?
• In order to produce the optimal amount of
HTML on the server for the best experience,
what we need in this scenario is some fine-
grained control over what gets rendered or
not on the server.
• We are going to implement that using a
couple of custom structural
directives: appShellRender and appShellNoRe
nder.
Example –use Application shell in app
component template
• In the main component, we might want to conditionally render a loading
indicator using appShellRender:
• <mat-sidenav-container fullscreen>
• ...
• <router-outlet></router-outlet>
•
• <div class="spinner-container" *appShellRender>
• <mat-spinner></mat-spinner>
• </div>
• </mat-sidenav-container>
• This means that a rendering indicator will be added to the bottom of each
page, but only if the rendering occurs on the server.
• appShellRender and appShellNoRender have no effect on the client! In the
browser, the whole template will be rendered each time as we navigate
through the single page application.
How appShellRender directive
implemented
• @Directive({
• selector: '[appShellRender]'
• })
• export class AppShellRenderDirective implements OnInit {
• constructor(
• private viewContainer: ViewContainerRef,
• private templateRef: TemplateRef<any>,
• @Inject(PLATFORM_ID) private platformId) {}
•
• ngOnInit() {
• if (isPlatformServer(this.platformId)) {
• this.viewContainer.createEmbeddedView(this.templateRef);
• }
• else {
• this.viewContainer.clear();
• }
• }
• }
How to generate directives
• To create a directive, use the CLI command ng generate directive.
• ng generate directive highlight
• The CLI creates src/app/highlight.directive.ts, a corresponding test file
src/app/highlight.directive.spec.ts, and declares the directive class in the
AppModule.
• src/app/highlight.directive.ts
• import {Directive} from '@angular/core';
• @Directive({
• standalone: true,
• selector: '[appHighlight]',
• })
• export class HighlightDirective {}
Adding Logic
• The @Directive() decorator's configuration property specifies the
directive's CSS attribute selector, [appHighlight].
• @Directive({
• standalone: true,
• selector: '[appHighlight]',
• })
• export class HighlightDirective {
• constructor(private el: ElementRef) {
• this.el.nativeElement.style.backgroundColor = 'yellow';
• }
• }
Applying the attribute directive
• To use the HighlightDirective, add a <p>
element to the HTML template with the
directive as an attribute.
• src/app/app.component.html
• <p appHighlight>Highlight me!</p>
Summary
• In angular application – we learned about
• Components, directives, services
• Module, NgModule, FormsModule, Common Module,
• Routing, Lazy Loading ,dynamic routing
• SSR, Client Hydration using lifecycle hooks
• Template syntaxes
• Angular Universal application architecture
• Application structure, configuration files
• SEO, Social Media optimization, App Shell
• Angular SSR, Universal version1, Universal Ivy