ilovepdf_merged

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 160

Session 9 & 10

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

bootstrap A method which returns an NgModule or a promise which resolves to


an ApplicationRef.

providers An array of platform level providers for the current request.


url The url of the page to render.
inlineCriticalCss Whether to reduce render blocking requests by inlining critical CSS. true

publicPath Base path for browser files and assets.


document The initial DOM to use for bootstrapping the server application.

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;

• // Perform client-side hydration tasks here


• // For example, attaching event listeners or manipulating the DOM

• // Example: Adding a CSS class


• hydratedContentElement.classList.add('hydrated');

• // Example: Attaching an event listener


• hydratedContentElement.addEventListener('click', () => {
• console.log('Element clicked!');
• });
• }
• }
• In this example:
• We import AfterViewInit from @angular/core to use
the ngAfterViewInit() lifecycle hook.
• Inside the component class HydrationComponent, we
implement the ngAfterViewInit() method.
• Within ngAfterViewInit(), we access the DOM element
using Angular's @ViewChild decorator with template
reference variable #hydratedContent.
• We perform client-specific tasks such as adding a CSS
class or attaching event listeners to the DOM element.
• When this component is used in an Angular application
with SSR, the ngAfterViewInit() method will be invoked
after the view has been initialized on the client-side,
allowing us to perform client-side hydration tasks
safely.
• This ensures that the component behaves consistently
across different rendering environments.

Example 2
import { Component, AfterViewInit } from '@angular/core';

• @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;

• // Perform client-side hydration tasks here


• // For example, attaching event listeners or manipulating the DOM

• // Example: Attaching an event listener


• buttonElement.addEventListener('click', () => {
• console.log('Button clicked!');
• });
• }

• // 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

• Create Universal application: After installing Angular Universal, you


can generate the Universal application using the following
command:
• ng generate universal --clientProject my-universal-app
• Error: Unknown argument: clientProject
New Angular Application creation
• D:\softwares\smallservices>ng generate
universal
• Error: A collection and schematic is required
during execution.
Building and Serving the application
• Build and serve your application: Once everything is set
up, you can build and serve your application with Angular
Universal by running:
• npm run build:ssr
• npm run serve:ssr
• These commands will build your Angular Universal
application and start a server to serve the pre-rendered
content.
• Now installed Angular Universal and created a new Angular
application with Universal support.
• Develop your application further and take advantage of
server-side rendering for improved performance and SEO.
Creating angularuniversal - ungcli
• 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.
• npm install -g universal-cli
• After you intalled with,create a new project with
• ung new PROJECT_NAME --universal
• Then the project should be ready to serve with
• cd PROJECT_NAME ung serve
Angular Universal Architecture
Modules & Components
• Modules: An Angular app has a root module,
named AppModule, which provides the
bootstrap mechanism to launch the
application.
• Each component in the application defines a
class that holds the application logic and data.
A component generally defines a part of the
user interface (UI).
Templates

• The Angular template combines the Angular


markup with HTML to modify HTML elements
before they are displayed. There are two types of
data binding:
• Event binding: Lets your app respond to user
input in the target environment by updating your
application data.
• Property binding: Enables users to interpolate
values that are computed from your application
data into the HTML.
Metadata & Services
• Metadata tells Angular how to process a class.
It is used to decorate the class
• It can configure the expected behavior of a
class.
• When you have data or logic that isn’t
associated with the view but has to be shared
across components, a service class is created.
The class is always associated with the
@Injectible decorator.
Dependency Injection

• This feature lets you keep your component


classes crisp and efficient. It does not fetch
data from a server, validate the user input, or
log directly to the console. Instead, it
delegates such tasks to the services.
• Angular comes with its own set of advantages
and disadvantages. The next two sections
briefly explain them.
Angular JS Directives
• AngularJS directives extend the HTML by providing it with
new syntax. You can easily spot directives because they
have the prefix “ng-.” Consider them markers on the DOM
element, instructing AngularJS to attach a certain behavior
to the element, or even change it outright.
• Here are two sample directives:
• The ng-model Directive
• The ng-model binds the value of the HTML control with the
specified AngularJS expression value.
• The ng-bind Directive
• This directive replaces the HTML control value with a
specified AngularJS expression value.
Creating Services
• Components shouldn't fetch or save data directly, and
they certainly shouldn't knowingly present fake data.
They should focus on presenting data and delegate
data access to a service.
• A Service can be created that all application classes can
use to get data.
• Instead of creating that service with the newkeyword,
use the dependency injection that Angular supports to
inject it into the Component constructor.
• Services are a great way to share information among
classes that don't know each other
• Run ng generate to create a service called hero.
• ng generate service hero
• The command generates a skeleton HeroService class
in src/app/hero.service.ts as follows:
• import { Injectable } from '@angular/core';

• @Injectable({
• providedIn: 'root',
• })
• export class HeroService {

• constructor() { }

• }
@Injectable() services

• The new service imports the Angular Injectable symbol


• Annotates the class with the @Injectable() decorator.
• This marks the class as one that participates in
the dependency injection system.
• The HeroService class is going to provide an injectable
service,
• It can also have its own injected dependencies. It
doesn't have any dependencies yet.
• The @Injectable() decorator accepts a metadata object
for the service, the same way
the @Component() decorator did for your component
classes.
Get hero data

• The HeroService could get hero data from


anywhere such as a web service, local storage,
or a mock data source.
• Removing data access from components
means you can change your mind about the
implementation anytime, without touching
any components.
• Components don't know how the service
works.
• src/app/hero.service.ts
• import { Hero } from './hero';
• import { HEROES } from './mock-heroes';
• Add a getHeroes method to return the mock
heroes.
• src/app/hero.service.ts
• getHeroes(): Hero[]
• { return HEROES; }
Provide the HeroServic
• To make sure that the HeroService can provide
this service, register it with the injector.
• The injector is the object that chooses and
injects the provider where the application
requires it.
• By default, ng generate service registers a
provider with the root injector for your service
by including provider metadata, that's
• providedIn: 'root' in
the @Injectable() decorator.
Provide the HeroServic

• When you provide the service at the root


level, Angular creates a single, shared instance
of HeroService and injects into any class that
asks for it.
• Registering the provider in
the @Injectable metadata also allows Angular
to optimize an application by removing the
service if it isn't used.
Update HeroesComponent

• Open the HeroesComponent class file.


• Delete the HEROES import, because you won't need that anymore.
Import the HeroService instead.

• src/app/heroes/heroes.component.ts (import HeroService)


• import { HeroService } from '../hero.service';

• Replace the definition of the heroes property with a declaration.

• src/app/heroes/heroes.component.ts

• heroes: Hero[] = [];


Inject the HeroService

• Add a private heroService parameter of type HeroService to the


constructor.

• src/app/heroes/heroes.component.ts

• constructor(private heroService: HeroService) {}

• The parameter simultaneously defines a private heroService


property and identifies it as a HeroService injection site.

• When Angular creates a HeroesComponent, the Dependency


Injection system sets the heroService parameter to the singleton
instance of HeroService.
Add getHeroes()

• Create a method to retrieve the heroes from


the service.
• src/app/heroes/heroes.component.t
• getHeroes(): void { this.heroes =
this.heroService.getHeroes(); }
Call it in ngOnInit()

• 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

• import { Injectable } from '@angular/core';


• import {Observable} from "rxjs"

@Injectable({
• providedIn: 'root'
• })
• export class DataserviceService {
• private datafile ="../assets/data1";

constructor() { }
• getdata():string[] {
• return ["wxc","xyx"];
• }
• }

Services Example 1
• In appcomponent –appcomponent.component.ts

• import {DataserviceService } from './dataservice.service'


• export class AppComponent {
• accessdata:string[];

constructor(private dataservice1:DataserviceService){
• this.isBrowser = typeof Window !== "undefined";
• console.log("isBrowser value "+this.isBrowser);
• }
• ngOnInit():void{
• this.getdata();
• }
• getdata():void{
• this.accessdata=this.dataservice1.getdata();
• console.log(this.accessdata)
• }


Services Example 1
• In client hydration component
• Clienthydrationcomponent.component.ts
• import { DataserviceService } from '../dataservice.service';
• export class ClienthydrationComponent {

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

• Enter the following command


where customer-app is the name of your app:
• ng new customer-app --no-standalone
• This creates an application called customer-
app with a file called app-routing.module.ts.
• This is one of the files you need for setting up
lazy loading for your feature module.
Create a feature module with routing

• We need a feature module with a component to


route to.
• To make one, enter the following command in the
command line tool, where customers is the name
of the feature module.
• The path for loading the customers feature
modules is also customers because it is specified
with the --route option:
• ng generate module customers --route customers
--module app.module
Create a feature module with routing

• This creates a customers directory having the


new lazy-loadable feature module
CustomersModule defined in the
customers.module.ts file
• The routing module CustomersRoutingModule
defined in the customers-routing.module.ts file.
• The command automatically declares the
CustomersComponent and imports
CustomersRoutingModule inside the new feature
module.
Create a feature module with routing

• Because the new module is meant to be lazy-


loaded, the command does not add a
reference to it in the application's root module
file, app.module.ts.
• Instead, it adds the declared
route, customers to the routes array declared
in the module provided as the --
module option.
Create a feature module with routing

• 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

• Use the same command to create a second lazy-loaded


feature module with routing, along with its stub
component.
• ng generate module orders --route orders --module
app.module
• This creates a new directory called orders containing
the OrdersModule and OrdersRoutingModule, along
with the new OrdersComponent source files.
• The orders route, specified with the --route option, is
added to the routes array inside the app-
routing.module.ts file, using the lazy-loading syntax.
src/app/app-routing.module.ts
• const routes: Routes = [ { path: 'customers',
loadChildren: () =>
import('./customers/customers.module').then
(m => m.CustomersModule) }, { path: 'orders',
loadChildren: () =>
import('./orders/orders.module').then(m =>
m.OrdersModule) } ];
Set up the UI
• Replace the default placeholder markup
in app.component.html with a custom nav, so you can
navigate to your modules in the browser:
• <h1>
• {{title}}
• </h1>
• <button type="button"
routerLink="/customers">Customers</button>
• <button type="button"
routerLink="/orders">Orders</button>
• <button type="button" routerLink="">Home</button>

• <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

• Verify that a module is indeed being lazy


loaded with the Chrome developer tools.
• In Chrome, open the developer tools by
pressing Cmd+Option+i and go to the Network
Tab.
Verify lazy loading
Verify lazy loading

• Click on the Orders or Customers button. If


you see a chunk appear, everything is wired
up properly
• The feature module is being lazy loaded. A
chunk should appear for Orders and for
Customers but only appears once for each.
Verify lazy loading
Session 14
Topics
• State Transfer APIs
• Configurations, routing, App, Module,
component
• Tasks
State Transfer APIs

• A key value store that is transferred from the


application on the server side to the
application on the client side.
• In SSR when you make API calls on the server
the data will be rendered and binded to the
HTML
• HTML template will be transfered to the client
and rendering happens again
Issue with SSR
• In Angular, State Transfer API is commonly associated with Angular
Universal, which is a technology that enables server-side rendering
(SSR) for Angular applications.
• When a user accesses an Angular Universal application, the server
renders the initial view of the application on the server side and
sends it to the client.
• This provides faster initial page loads and better search engine
optimization (SEO) compared to traditional client-side rendering
(CSR) applications.
• However, when the application transitions from server-rendered
content to client-rendered content, there can be a loss of state. For
example, if a user fills out a form on a server-rendered page, and
then navigates to another page that is client-rendered, the form
data might be lost.
To address this issue
• Angular Universal provides the State Transfer API. This API allows you to
transfer state between the server and the client during the transition from
server-side rendering to client-side rendering, ensuring that the
application maintains its state integrity.
• Here's how the State Transfer API works in Angular Universal:
• Server-side rendering: When the Angular Universal server renders the
initial view of the application, you can capture the application state using
the State Transfer API.
• Transferring state to the client: The captured state is serialized and
embedded into the server-rendered HTML response sent to the client.
• Client-side rendering: When the client receives the server-rendered HTML
response, it parses the HTML and extracts the transferred state.
• Hydration: The extracted state is hydrated into the client-side Angular
application, allowing the application to continue from the same state
where the server left off.
What State tranfer API Does
• By using the State Transfer API, Angular
Universal ensures that the application state is
maintained across the server-client transition,
providing a seamless user experience.
• This is particularly important for preserving
form data, authentication state, or any other
client-side state that needs to persist across
server-client transitions.
Example
• import { Component, Inject, OnInit, PLATFORM_ID, TransferState, makeStateKey } from
'@angular/core';
import { CommonModule, isPlatformBrowser, isPlatformServer } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { HttpClient, HttpClientModule } from '@angular/common/http';
const dataKey = makeStateKey<{data:string}>("data")
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet, HttpClientModule],
template: `{{bindingData?.data}}`,
styles: []
})
export class AppComponent implements OnInit{
bindingData?:{data:string};
constructor(@Inject(PLATFORM_ID) private platformID: Object, private httpClient:
HttpClient,private transferState:TransferState){
}
Example (cont)
• ngOnInit(): void {
if(isPlatformServer(this.platformID)){ //<--- this block run on server
console.log("this block runs only on server");
this.httpClient.get("http://localhost:8080/data").subscribe((r:any)=>{
this.bindingData=r;
this.transferState.set(dataKey,r); //<--- add this line to save the state
console.log("data is rendered",r);
})
}
else if(isPlatformBrowser(this.platformID)){
console.log("this block runs only on client");
console.log("client site retrived data before ",this.bindingData)
this.bindingData=this.transferState.get<{data:string}>(dataKey,{data:""});
console.log("client site retrived data after ",this.bindingData)
}
}
}
Important Configuration Files
• angualor.json
• package.json
• tsconfig.json
angular.json
• The angular.json file at the root level of an
Angular workspace provides workspace-wide
and project-specific configuration defaults.
• These are used for build and development
tools provided by the Angular CLI.
• Path values given in the configuration are
relative to the root workspace directory.
Examples –In angular.json
• …
• "architect": {
• "build": {
• …
• "options": {
• …
• "styles": [
• "node_modules/bootstrap/scss/bootstrap.scss",
• …
• ],
• "scripts":
• [
• "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js”,
• …
• ],
• "serve": {
• "options": {
• "port": 4100
• }

• …
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

• One of the biggest improvements from the new architecture is the


size of the compiled bundles.
• This was achieved by removing dormant parts of Angular that are
not used through tree-shaking, thus generating less code per
component.
• This benefits both small apps as they do not use many features so
tree-shaking works well and even large apps from reduced factory
size.
• The Ivy compiler has been designed to remove parts of Angular that
aren’t being used via tree-shaking and to generate less code for
each Angular component.
• Small apps could see around a 30% decrease in bundle size, while
large apps will see a 25–40% decrease, and medium apps decrease
minimally
Build Time Improvements, Faster AOT
Compiler
• A lot has changed with the Angular compiler with respect to performance,
the team found that a 40% improvement was recorded on the Ivy
architecture.
• This was measured by the overhead recorded side by side with plain
TypeScript app compilation.
• Good changes like this also mean that the Ahead-of-Time builds are now
faster!
• Your dev server command “ng serve” now runs like it’s in production, with
similar compile times, or even faster.
• This means you see actual improvements right from the development
stage.
• With Ivy, your ngModules do not need entryComponents to work. These
entryComponents were used to tell the compiler the components to
update or create, but they’re no longer necessary and you can take them
off module declarations once you upgrade to Version 9 or above.
More Useful Error Messages
• There’s been a lot of improvement with the
developer experience in Angular.
• With Angular Ivy, error messages are now more
readable.

• Better clarity and suggestions which are super


helpful and reduce debugging time exponentially.
Internationalization and Type Checking
Updates
• Proper internationalization (i18n) from the very beginning can save
you from potential pitfalls and support the future growth of your
product.
• With Ivy the way to build apps per locale is now even faster as the
build time substitutions have been moved to the build process.
• Type checking has also now been improved with two more flags
that make your workflow even easier.
• fullTemplateTypeCheck – This flag informs the compiler to check
everything inside your template (ngIf, ngFor, ng-template, etc.)
• strictTemplates – Turning this flag on will apply the strictest Type
System rules for type checking.
Style Binding Updates

• The new Ivy compiler also ships with improved ways of


resolving conflicting style bindings, styles are now
merged in a more intuitive and predictable way.
• Before Ivy, the timing of the style binding was super
important and the last defined style always wins.
• Now, you can manage styles through your own defined
order of precedence by being super specific,
• For instance [style.font-size] would take precedence
over [style].
Richer Template HTML syntax
• All html element can be used except
script,body,html,base
• Extend the HTML vocabulary of your
templates with components and directives
that appear as new elements and attributes
• get and set DOM (Document Object Model)
values dynamically through data binding.
Interpolation

• Interpolation allows you to incorporate calculated


strings into the text between HTML element tags and
within attribute assignments.
• Interpolation refers to embedding expressions into
marked up text. By default, interpolation uses as its
delimiter the double curly braces, {{ and }}.
• The text between the braces is often the name of a
component property.
• Angular replaces that name with the string value of the
corresponding component property.
• interpolation is a special syntax that Angular converts
into a property binding.
Template Expressions
• Template expressions are what you use to calculate
those strings.
• The text between the braces is a template
expression that Angular first evaluates and
then converts to a string.
• <!-- "The sum of 1 + 1 is 2" --> <p>The sum of 1 + 1 is
{{1 + 1}}.</p>
• Angular evaluates all expressions in double curly
braces, converts the expression results to strings, and
links them with neighboring literal strings.
• Finally, it assigns this composite interpolated result to
an element or directive property.
Template Expressions
• In the property binding, a template
expression appears in quotes to the right of
the = symbol as in [property]="expression".
• In terms of syntax, template expressions are
similar to JavaScript.
• Many JavaScript expressions are legal
template expressions, with a few exceptions.
Interpolation Examples
• <!-- Interpolation and expressions -->
• <hr><h2 id="interpolation">Interpolation</h2>

<p>My current hero is {{currentHero.name}}</p>

<h3>
• {{title}}
• <img src="{{heroImageUrl}}" style="height:30px">
• </h3>

<!-- "The sum of 1 + 1 is 2" -->
• <p>The sum of 1 + 1 is {{1 + 1}}</p>

<!-- "The sum of 1 + 1 is not 4" -->
• <p>The sum of 1 + 1 is not {{1 + 1 + getVal()}}</p>

Javascript expression exceptions
• Assignments (=, +=, -=, ...)
• Operators such as new, typeof, instanceof, etc.
• Chaining expressions with ; or ,
• The increment and decrement
operators ++ and --
• Some of the ES2015+ operators
• No support for the bitwise operators such
as | and &
Expression Context
• The expression context is typically
the component instance.
• In the following snippets,
the recommended within double curly braces and
the itemImageUrl2 in quotes refer to properties
of the AppComponent.
• An expression may also refer to properties of
the template's context such as a template input
variable
• let customer, or a template reference
variable, #customerInput.
Example
• Expression component context
• <h4>{{recommended}}</h4>
• <img [src]="itemImageUrl2">
• Expression template context
• Let a template reference variable, #customer.
• <label>Type something:
• <input #customer>{{customer.value}}
• </label>
• The customer in {{customer.name}} refers to the
template input variable, not the component's
property.
Example
• <ul> <li *ngFor="let customer of
customers">{{customer.name}}</li> </ul>
• The component has a customer property and
the *ngFor defines a customer template
variable.
Expression Limitations
• Template expressions cannot refer to anything
in the global namespace, except undefined.
They can't refer to window or document.
Additionally, they can't
call console.log() or Math.max() and they are
restricted to referencing members of the
expression context.
Template Statements
• A template statement responds to
an event raised by a binding target such as an
element, component, or directive.
• Template statements in the event
binding section, appearing in quotes to the
right of the = symbol as
in (event)="statement".
• button (click)="deleteHero()">Delete
hero</button>
HTML attribute vs. DOM property

• The distinction between an HTML attribute and a DOM property is


key to understanding how Angular binding works.
• Attributes are defined by HTML. Properties are accessed from DOM
(Document Object Model) nodes.
• A few HTML attributes have 1:1 mapping to properties; for
example, id.
• Some HTML attributes don't have corresponding properties; for
example, aria-*.
• Some DOM properties don't have corresponding attributes; for
example, textContent.
• It is important to remember that HTML attribute and the DOM
property are different things, even when they have the same name.
In Angular, the only role of HTML attributes is to initialize element
and directive state.
How Template binding works
• Template binding works
with properties and events, not attributes.
• When you write a data-binding, you're dealing
exclusively with the DOM
properties and events of the target object.
• <input type="text" value="Sarah">
• When the browser renders <input type="text"
value="Sarah">, it creates a corresponding DOM
node with a value property initialized to "Sarah"
How Template binding works
• When the user enters "Sally" into the <input>,
the DOM element value property becomes
"Sally".
• However, if you look at the HTML
attribute value using input.getAttribute('value'),
you can see that the attribute remains
unchanged—it returns "Sarah".
• The HTML attribute value specifies
the initial value; the DOM value property is
the current value.
Session 16
Topics
• Search Engine optimization
• Social Media optimization
• PowerShell
• Directives
Search Engine optimization

• Reason for doing server-side rendering is to


make our application more search engine
friendly
• Today, most search engines take the title and
description shown in search results from
metadata tags present in the header section
of the page.
Example
• <head>
• <meta charset="UTF-8">
• <meta name="description" content="Free Web
tutorials">
• <meta name="keywords" content=“Angular
HTML, CSS, JavaScript">
• <meta name="author" content=“Meenakshi">
• <meta name="viewport"
content="width=device-width, initial-scale=1.0">
• </head>
Google search result: “Angular
Universal”
Where do these page titles come
from?
• All the blue link titles that we see in these
search results are being filled in based on
metadata tags present on the pages they link
to.
• For example, the title for our third search
result (highlighted in red) can be found in a
title HTML tag, present inside
the head section of the result page:
Example : title in search results
What do search engines expect to
find in a page?
• Most search engine crawlers expect these important
SEO meta tags to be present on the HTML returned by
the server, and not to modified at runtime by
Javascript.
• And this is the same for the remainder of the page
content, most search engines will index only the
content that comes back from the server directly, and
not what get's loaded using Javascript.
• So having our page server-side render those metadata
tags is essential for ranking correctly in a lot of search
engines.
Does the Google Search Engine index
well single page applications?
• Currently, the Google search engine indexes
correctly most Javascript pages
• A good proof of that is the Angular Docs site,
which is itself an SPA built with Angular.
• The Angular Docs site indexes perfectly for long
queries that target content that is loaded
dynamically via Javascript.
• The website even populates the title and
description meta tags dynamically using
Javascript , and those get shown in the search
results with no problem.
Long Query Search
As we can see, the Angular Docs SPA ranks perfectly for this very long search query
on Google, even though all its content was loaded dynamically by Javascript.
Do all search engines index
Javascript?
• Search Query: Producing an error also causes
the observable to clean up subscriptions and
stop producing values
Do all search engines index
Javascript?
• As we can see the same Angular Docs result
that is in third place on Google does even not
rank on Bing, as Bing currently does not index
dynamic Javascript content.
• The same is true for many search engines
including DuckDuckGo and others.
Is SEO still a key reason for using
Angular Universal?
• If we are targetting only the Google search
engine, as we have shown there is no need to
server-side render our content in order to have it
ranked correctly, as Google can today index
correctly most Javascript-based content.
• On the other hand, if we want to target all search
engines, then server-side rendering is a must as
we can see in Bing search results.
• One last reason for using Angular Universal:
Social Media crawlers.
Angular Universal SEO - Search Engine
Optimization – with application
• We start optimizing our application for SEO. We
will go ahead and set the title tag and description
for example for the Course page.

• We can adapt the SEO metadata depending on


the page that we are currently on.

• In this case, set the title tag of the page to the


title of the course and populate also the
description meta tag. We can do that using the
Title and Meta services:
Example
• @Component({
• selector: 'course',
• templateUrl: './course.component.html',
• styleUrls: ['./course.component.css']
• })
• export class CourseComponent implements OnInit {

• course: Course;

• constructor(private title: Title,


• private meta: Meta) {}

• 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.

• The CLI generates the default src/app/highlight.directive.ts as follows:

• 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].

• Import ElementRef from @angular/core. ElementRef grants direct


access to the host DOM element through its nativeElement
property.

• Add ElementRef in the directive's constructor() to inject a reference


to the host DOM element, the element to which you apply
appHighlight.

• Add logic to the HighlightDirective class that sets the background to


yellow.
Adding Logic –to directive
appHighlight
• import {Directive, ElementRef} from '@angular/core';

• @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

You might also like