Angular
Angular
1) What is Angular ?
• A JavaScript framework for building application using web technologies like html, css and
js.
• Empowers developers to build applications for browsers, mobiles, or desktop.
Angular History:
Developed by Google in 2009, Angular version 1(angular js), 2, 4,5,6,7,8,9,10,11,12,…15 so
on,.
NPM – node package manager, is included with Node. js installation
Angular CLI
• A powerful to create, build, compile and serve Angular2 App
• Used to generate new components, routes, services and pipes
• Command to install angular cli - npm install -g @angular/cli
Modules
An Angular module is a class with an @NgModule decorator
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { FooterComponent } from './footer/footer.component';
import { UserService } from './services/user.service';
import { CapitalizePipe } from './pipes/capitalize.pipe'; // Importing the pipe
import { HoverHighlightDirective } from './directives/hover-highlight.directive'; //
Importing the directive
@NgModule({
declarations: [
AppComponent,
HeaderComponent,
FooterComponent,
CapitalizePipe, // Declaring the pipe
HoverHighlightDirective // Declaring the directive
],
imports: [
BrowserModule,
FormsModule//two way data binding
],
providers: [
UserService
],
bootstrap: [AppComponent]
})
export class AppModule { }
Decorators
• Adds metadata to a class, method, property, or parameter. These are prefix with @ symbol
• Ex: @Component, @NgModule, @Directive, @Pipe etc.
Here, the metadata means:
selector: The custom HTML tag for the component.
templateUrl: The path to the component’s HTML template.
styleUrls: The path to the component’s CSS styles.
2) SPA VS MPA
SPA – Single page application (ex: gmail, google, fb, twitter, github)
• When you visit a specific web page in an SPA, the browser sends a request to the server.
• For the first request, the server sends back an HTML document.
• For subsequent requests, the server sends JSON data instead of a full HTML page.
• The SPA then rewrites the current page’s content using the received data, avoiding the
need to reload the entire web page.
• This behavior makes SPAs feel like native applications, with faster performance and
smoother navigation.
MPA – Multi page application (ex: amazon, ebay, udemy)
• In an MPA, each web page is treated as a separate entity.
• When a user interacts with the application (e.g., clicks a link, submits a form), the server
processes the request and sends back a completely new HTML page.
• The browser then replaces the current page with the newly received page, resulting in a
full page reload.
• This approach prioritizes simplicity and straightforwardness but can lead to slower user
experiences due to frequent page reloads.
3) Angular Typescript
• Super set of JavaScript that compiles into JavaScript
• Developed by Microsoft and released 1.0 version in 2012
• Free and open source programming language
• Advantages: uses both interfaces & classes, Easy to adopt for object oriented developers
(like java & C#) and open source.
Datatypes:
Any, Built-in - number string, Boolean, void, null, undefined,
User Defined - class, interface, Enum, array, function.
Variables:
Var - have function scope or global scope.
var num1 = 1; // Global scope
function exampleFunction() {
var num2 = 2; // Function scope
console.log(num1); // Accessible
console.log(num2); // Accessible
}
exampleFunction();
console.log(num1); // Accessible
console.log(num2); // Error: Cannot find name 'num2'
let - uses block scope
function exampleFunction() {
if (true) {
let x = 10; // x is block-scoped within this if block
console.log(x); // Output: 10
}
// console.log(x); // Error: x is not accessible here
}
exampleFunction();
const – uses block scope
if (true) {
const pi = 3.14; // pi is block-scoped within this if block
// pi = 3.14159; // Error: Cannot reassign a constant variable
console.log(pi); // Output: 3.14
}
Note - difference between let and const is, let can be re-assigned but const can’t
Class Structure as that of C#:
class Student {
private rollNo: number;
private name: string;
constructor(_rollNo: number, _name: string) {
this.rollNo = _rollNo;
this.name = _name;
}
showDetails() { //public : by default
console.log(this.rollNo + " : " +
this.name);
}
}
let s1 = new Student(1, "Shailendra Chauhan");
s1.showDetails(); //1 : Shailendra Chauhan
let s2 = new Student(2, "kanishk Puri");
s2.showDetails(); //2 : kanishk Puri
4) Important Concepts
a. Databinding
A mechanism for binding data values to HTML elements and turning user activities into
actions.
b. Directives
Change the appearance or behavior of a DOM element.
Component Directives
These are the most common type of directives. They are essentially Angular components,
which include a template and encapsulated logic.
Example
TypeScript
import { Component } from '@angular/core';
@Component({
selector: 'app-hello',
template: `<h1>Hello, {{name}}!</h1>`,
})
export class HelloComponent {
name = 'Angular';
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
viewMode = 'default';
setViewMode(mode: string) {
this.viewMode = mode;
}
}
Attribute Directives –
ngClass
ngStyle
ngModel
ngClass
The ngClass directive allows you to dynamically add or remove CSS classes to an element.
Example
HTML
<div [ngClass]="{'active': isActive, 'disabled': isDisabled}">
This div can have active and disabled classes.
</div>
<button (click)="toggleActive()">Toggle Active</button>
<button (click)="toggleDisabled()">Toggle Disabled</button>
TypeScript
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
isActive = true;
isDisabled = false;
toggleActive() {
this.isActive = !this.isActive;
}
toggleDisabled() {
this.isDisabled = !this.isDisabled;
}
}
In this example, ngClass dynamically applies the active and disabled classes based on the
values of isActive and isDisabled.
ngStyle
The ngStyle directive allows you to set inline styles dynamically.
Example
HTML
<div [ngStyle]="{'color': textColor, 'font-size': fontSize}">
This text can change color and size.
</div>
<button (click)="changeColor()">Change Color</button>
<button (click)="changeFontSize()">Change Font Size</button>
TypeScript
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
textColor = 'blue';
fontSize = '16px';
changeColor() {
this.textColor = this.textColor === 'blue' ? 'red' : 'blue';
}
changeFontSize() {
this.fontSize = this.fontSize === '16px' ? '20px' : '16px';
}
}
Here, ngStyle dynamically sets the color and font-size styles based on the values
of textColor and fontSize.
ngModel
The ngModel directive binds an input, select, or textarea element to a property on the
component.
Pre-requisites - In your app.module.ts file, you need to import FormsModule and add it to
the imports array:
Example
HTML
<input [(ngModel)]="name" placeholder="Enter your name">
<p>Hello, {{name}}!</p>
TypeScript
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
name = '';
}
In this example, ngModel binds the input field to the name property, allowing two-way data
binding.
Two-way data binding means that any changes in the template are reflected in the
component class and vice versa
Custom directive :
in Angular allow you to create reusable components and add behavior to HTML elements
Should be registered in app.module.ts by adding in declarations
HTML:
<div appHoverHighlight >Hover over me!</div>
Custom directive:
import { Directive, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appHoverHighlight]'
})
export class HoverHighlightDirective {
constructor(private el: ElementRef, private renderer: Renderer2) { }
@HostListener('mouseenter') onMouseEnter() {
this.highlight('yellow');
}
@HostListener('mouseleave') onMouseLeave() {
this.highlight(null);
}
c. Pipes or Filters
• Used to transform bound properties before displaying.
• built-In Pipes are : Lowercase ▪ Uppercase ▪ Date ▪ Currency ▪ Json ▪ Decimal
Custom Pipe : Should be registered in app.module.ts by adding in declarations
Code example
Comp.html
<p>The transformed value is: {{ inputValue | custom }}</p>
custom.pipe.ts
Pure Pipe
default pipe, executes when input data changes
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'custom' // Pipe name used within components
pure: true, // Pure pipe (default behavior)
})
export class CustomPipe implements PipeTransform {
transform(value: any, ...args: any[]): any {
// Your transformation logic here
return value.toUpperCase();
}
}
Impure Pipe
Executes on every change detection cycle, even if its input data hasn’t changed.
Real time example – search
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'filter',
pure: false, // Impure pipe
})
export class FilterPipe implements PipeTransform {
transform(items: any[], searchText: string): any[] {
if (!items) return [];
if (!searchText) return items;
searchText = searchText.toLowerCase();
return items.filter(it => {
return it.name.toLowerCase().includes(searchText);
});
}
}
In general, you should prefer pure: true for performance reasons unless there's a specific
need for pure: false like change detection cycle.
d. Angular Routing
• It is, Every time when you request a Url, the Angular router navigates to the new
component and renders its template.
Routing Configuration
• Describe routes in app-routing.module.ts
const routes: Routes = [
{ path: '', component: HomePageInformationComponent },
{ path: 'auth', component: MsalRedirectComponent },
{ path: 'Reports/DailySummaryInput', component: DailyReportComponent }
];
• RouterOutlet is where the router render the component
<app-header></app-header>
<router-outlet></router-outlet>
• RouterLink a link to a route name, router link defined in header component and matches
with defined route in app-routing.module.ts
<li><a class="dropdown-item" (click)="mainheaderdailyreport()"
routerLink="Reports/DailySummaryInput">Daily
Report</a>
</li>
Route Parameters:
Used to pass data through route paths
Mainly there are following ways :
1. Required Parameters
2. Optional Parameters
3. Query Parameters
Child routes :
const routes: Routes = [
{
path: 'product',
component: ProductComponent,
children: [
{ path: 'detail/:id', component: ProductDetailComponent }
]
}
];
In the above example:
The parent route path is /product.
The child route path is /product/detail/:id.
When the user navigates to /product/detail/123, Angular will render the
ProductDetailComponent.
e. Interceptors
In Angular, an interceptor is a mechanism to intercept and modify HTTP requests or responses in
your application. Interceptors are implemented as services and are part of the Angular HttpClient
module.
auth-interceptor
@Injectable()
export class AuthInterceptorService implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Add an Authorization header to every request
const token = 'your-auth-token'; // Replace with your token logic
if (token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
@Injectable()
export class MyInterceptorService implements HttpInterceptor {
f. Guards
Guards in Angular are used to control a route. They are essentially services that implement one or
more guard interfaces to decide whether a route can be activated, deactivated, loaded, or unloaded.
5) Nested Components
@Input Decorator – parent to child communication
@Output Decorator – child to parent communication
@Input Decorator
Child component
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-child',
template: `<p>{{ childMessage }}</p>`
})
export class ChildComponent {
@Input() childMessage: string;
}
Parent Component:
import { Component } from '@angular/core';
@Component({
selector: 'app-parent',
template: `<app-child [childMessage]="parentMessage"></app-child>`
})
export class ParentComponent {
parentMessage = 'Hello from Parent!';
}
@Output Decorator
Child component
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child',
template: `<button (click)="sendMessage()">Send Message</button>`
})
export class ChildComponent {
@Output() messageEvent = new EventEmitter<string>();
sendMessage() {
this.messageEvent.emit('Hello from Child!');
}
}
Parent Component:
receiveMessage($event: string) {
this.message = $event;
}
}
6) Content Projection
@Component({
selector: 'app-child',
template: `
<div class="header">
<ng-content select="[header]"></ng-content>
</div>
<div class="body">
<ng-content select="[body]"></ng-content>
</div>
<div class="footer">
<ng-content select="[footer]"></ng-content>
</div>
`
})
export class ChildComponent {}
Parent Component:
attribute selectors
import { Component } from '@angular/core';
@Component({
selector: 'app-parent',
template: `
<app-child>
<h1 header>Header Content</h1>
<p body>Body Content</p>
<footer footer>Footer Content</footer>
</app-child>
`
})
export class ParentComponent {}
7) @ViewChild
The @ViewChild decorator in Angular is used to access a child component, directive, or DOM
element from a parent component class.
Ex 1:
Child Component:
import { Component } from '@angular/core';
@Component({
selector: 'app-child',
template: `<p>Hello from Child Component!</p>`
})
export class ChildComponent {
sayHello() {
console.log('Hello from Child Component!');
}
}
Parent Component:
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChildComponent } from './child.component';
@Component({
selector: 'app-parent',
template: `<app-child></app-child>
<button (click)="callChildMethod()">Call Child Method</button>`
})
export class ParentComponent implements AfterViewInit {
@ViewChild(ChildComponent) childComponent!: ChildComponent;
ngAfterViewInit() {
// Access child component's method after view initialization
// lifecycle hook ensures that the child component is fully initialized before accessing its
methods.
this.childComponent.sayHello();
}
callChildMethod() {
this.childComponent.sayHello();
}
}
Ex 2:
You can also use @ViewChild to access a DOM element using a template reference variable:
HTML
<input #inputElement type="text" placeholder="Enter text">
<button (click)="logInputValue()">Log Input Value</button>
Component:
import { Component, ViewChild, ElementRef } from '@angular/core';
@Component({
selector: 'app-example',
templateUrl: './example.component.html'
})
export class ExampleComponent {
@ViewChild('inputElement') inputElement!: ElementRef;
logInputValue() {
console.log(this.inputElement.nativeElement.value);
}
}
Difference between pipes and directives
Pipes: Transform data in templates, used with the pipe (|) symbol, stateless.
Directives: Add behavior to DOM elements, used as attributes, can be stateful.
@Injectable({
providedIn: 'root',
})
export class MessageService {
private messageSubject = new BehaviorSubject<string>(''); // Default message is
empty
message$ = this.messageSubject.asObservable(); // Observable for message
updates
sendMessage(message: string) {
this.messageSubject.next(message); // Emit the new message
}
clearMessage() {
this.messageSubject.next(''); // Clear the message
}
}
// message-sender.component.ts
import { Component } from '@angular/core';
import { MessageService } from './message.service';
@Component({
selector: 'app-message-sender',
template: `
<h2>Message Sender</h2>
<input [(ngModel)]="message" placeholder="Type a message" />
<button (click)="sendMessage()">Send Message</button>
`,
})
export class MessageSenderComponent {
message: string = ''; // Message input
sendMessage() {
this.messageService.sendMessage(this.message); // Send the message via the
service
this.message = ''; // Clear the input after sending
}
}
// message-receiver.component.ts
import { Component } from '@angular/core';
import { MessageService } from './message.service';
@Component({
selector: 'app-message-receiver',
template: `
<h2>Message Receiver</h2>
<div *ngIf="message">Received Message: {{ message }}</div>
<div *ngIf="!message">No messages received yet.</div>
`,
})
export class MessageReceiverComponent {
message: string = ''; // Message received
What is Zone.js?
@Component({
selector: 'app-counter',
template: `
<h1>Counter: {{ counter }}</h1>
<button (click)="resetCounter()">Reset Counter</button>
`,
})
export class CounterComponent {
counter = 0;
resetCounter() {
this.counter = 0;
}
}
1. OnPush Change Detection Strategy (between the components)
@Component({
selector: 'app-product-card',
template: `
<div>
<h3>{{ product.name }}</h3>
<p>Price: {{ product.price }}</p>
<button (click)="addToCart()">Add to Cart</button>
<button (click)="updatePrice()">Update Price</button>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush // Activates OnPush
strategy
})
export class ProductCardComponent {
@Input() product: { name: string; price: number }; // Receives product data
from the parent
@Output() priceUpdate = new EventEmitter<void>(); // Emits an event
when the price is updated
addToCart() {
console.log(`${this.product.name} added to cart!`); // Logs to console
when button clicked
}
updatePrice() {
this.priceUpdate.emit(); // Triggers the update price event
}
}
@Component({
selector: 'app-root',
template: `
<app-product-card
*ngFor="let item of products"
[product]="item"
(priceUpdate)="updatePrice(item)">
</app-product-card>
`,
})
export class AppComponent {
products = [
{ name: 'Laptop', price: 1000 },
{ name: 'Phone', price: 500 }
];
updatePrice(product) {
// Update the product price by increasing it by a fixed amount, e.g., 200
product.price += 200;
this.products = [...this.products]; // Trigger change detection for OnPush
}
}
Result
With this setup:
Each ProductCardComponent has its own "Update Price" button.
Clicking "Update Price" updates the price only for that specific product card without
re-rendering the others.
2. OnPush Change Detection Strategy and trackby
Above can be achieved by using single component only, by using OnPush
Change Detection Strategy and trackby
@Component({
selector: 'app-product-list',
template: `
<div *ngFor="let product of products; trackBy: trackByProductId">
<h3>{{ product.name }}</h3>
<p>Price: {{ product.price }}</p>
<button (click)="addToCart(product)">Add to Cart</button>
<button (click)="updatePrice(product)">Update Price</button>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush // Optimizes performance
})
export class ProductListComponent {
products = [
{ id: 1, name: 'Laptop', price: 1000 },
{ id: 2, name: 'Phone', price: 500 }
];
addToCart(product) {
console.log(`${product.name} added to cart!`);
}
updatePrice(product) {
// Updates the price for the selected product
product.price += 200;
this.products = [...this.products]; // Triggers change detection for OnPush
}
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class HomeRoutingModule {}
@Component({
selector: 'app-home',
template: `<h1>Home Page</h1>
<a routerLink="/feature">Go to Feature Page</a>`,
})
export class HomeComponent {}
Project Structure
Here’s a suggested structure for the E-Commerce application:
e-commerce-app/
│
├── src/
│ ├── app/
│ │ ├── cart/
│ │ │ ├── cart-routing.module.ts
│ │ │ ├── cart.module.ts
│ │ │ ├── cart.component.ts
│ │ │ └── cart.service.ts
│ │ │
│ │ ├── order/
│ │ │ ├── order-routing.module.ts
│ │ │ ├── order.module.ts
│ │ │ ├── order.component.ts
│ │ │ └── order.service.ts
│ │ │
│ │ ├── product/
│ │ │ ├── product-routing.module.ts
│ │ │ ├── product.module.ts
│ │ │ ├── product.component.ts
│ │ │ └── product.service.ts
│ │ │
│ │ ├── user/
│ │ │ ├── user-routing.module.ts
│ │ │ ├── user.module.ts
│ │ │ ├── user.component.ts
│ │ │ └── user.service.ts
│ │ │
│ │ ├── app-routing.module.ts
│ │ ├── app.module.ts
│ │ └── app.component.ts
│ │
│ ├── assets/
│ └── index.html
│
└── angular.json
Real time scenario - user related information like user id, static data which
won’t changes at DB end
But, after refresh cache gets cleared and data retrieves from server again
Cache Lifetime:
Data persists in the cache for the entire lifespan of the application's
session in the browser (i.e., until the user refreshes or closes the
browser tab, we have to explicitly clear
This is only for small datasets, avoid storing large datasets as it leads to
performance issue.
Template-Driven Forms
This form is simple to use and is typically recommended for straightforward
forms that don't require extensive custom validations or dynamic fields.
Configuration
App.module.ts
import { FormsModule } from '@angular/forms'; // Import FormsModule
imports: [ BrowserModule, FormsModule // Add FormsModule to imports ],
-------------------------------------------------------------------------
<form #registrationForm="ngForm"
(ngSubmit)="onSubmit(registrationForm)">
<div>
<label for="username">Username:</label>
<input
type="text"
id="username" <!-- 'id' for label association -->
name="username" <!-- 'name' for Angular to track the form control
-->
ngModel <!-- enables two-way data binding -->
required
minlength="3"
#username="ngModel" <!-- local template reference for validation -->
[class.modified]="username.dirty"
/>
<div *ngIf="username.invalid && username.touched">
<small *ngIf="username.errors?.required">Username is required.</small>
<small *ngIf="username.errors?.minlength">Username must be at least 3
characters.</small>
</div>
</div>
<div>
<label for="email">Email:</label>
<input
type="email"
id="email" <!-- 'id' for label association -->
name="email" <!-- 'name' for Angular to track the form control --
>
ngModel
required
email
#email="ngModel"
/>
<div *ngIf="email.invalid && email.touched">
<small *ngIf="email.errors?.required">Email is required.</small>
<small *ngIf="email.errors?.email">Please enter a valid email
address.</small>
</div>
</div>
<div>
<label for="password">Password:</label>
<input
type="password"
id="password" <!-- 'id' for label association -->
name="password" <!-- 'name' for Angular to track the form control
-->
ngModel
required
minlength="6"
#password="ngModel"
/>
<div *ngIf="password.invalid && password.touched">
<small *ngIf="password.errors?.required">Password is required.</small>
<small *ngIf="password.errors?.minlength">Password must be at least 6
characters.</small>
</div>
</div>
@Component({
selector: 'app-registration',
templateUrl: './registration.component.html',
})
export class RegistrationComponent {
onSubmit(form: any) {
if (form.valid) {
console.log('Form Submitted!', form.value);
}
}
}
-------------------------------------------------------------------------
.modified {
border: 1px solid orange; /* Highlights modified fields */
}
Configuration
App.module.ts
-------------------------------------------------------------------------
<label for="email">Email:</label>
<input id="email" formControlName="email">
<div *ngIf="myForm.get('email').invalid && (myForm.get('email').touched ||
myForm.get('email').dirty)">
<small *ngIf="myForm.get('email').errors?.required">Email is
required.</small>
<small *ngIf="myForm.get('email').errors?.email">Enter a valid
email.</small>
</div>
-------------------------------------------------------------------------
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
myForm: FormGroup;
ngOnInit(): void {}
onSubmit() {
if (this.myForm.valid) {
console.log('Form Submitted!', this.myForm.value);
}
}
}
Key terms
FormGroup: A container for managing multiple form controls
together.
formControlName: A directive to link input fields in your HTML to
specific controls in a FormGroup.
FormBuilder: A service to help create form controls and groups in a
simpler and cleaner way.
-------------------------------------------------------------------------
Validations and Control state in angular reactive forms
Validators
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
Summary
Template-Driven Forms: Simpler, less boilerplate, suitable for small forms.
Reactive Forms: More control, better for complex forms, explicit validation.
Navigate to Azure Active Directory > App registrations > New registration.
o Name: Enter a name for your app (e.g., "My Angular App").
o Supported account types: Choose which accounts can use the application (e.g.,
"Accounts in this organizational directory only").
o Redirect URI: Set the redirect URI to match your Angular app's URL with /auth-
callback (e.g., http://localhost:4200/auth-callback).
After registration, note down the Application (client) ID and Directory (tenant) ID from the
app overview page.
Choose Microsoft Graph and select the permissions needed for your application (e.g.,
User.Read for basic profile access).
typescript
// src/auth-config.ts
auth: {
};
};
Replace your-client-id and your-tenant-id with your actual Application (client) ID and Directory
(tenant) ID from Azure AD.
typescript
// src/app/app.module.ts
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
MsalModule.forRoot(
),
],
})
export class AppModule {}
typescript
// src/app/app-routing.module.ts
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
typescript
// src/app/app.component.ts
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
isLoggedIn = false;
ngOnInit() {
this.authService.instance.handleRedirectPromise().then((response) => {
});
login() {
this.authService.loginRedirect();
logout() {
1. Initialization: The component initializes and subscribes to router events to check the user's
authentication status.
4. Handle Login Events: The component subscribes to MSAL login success events. Upon
successful login, it sets the active account and updates the authentication status and user
info.
5. Load Menu and Roles: The LoadMenuSubMenus() method fetches the application menu
items, while GetUserRoleMembers() retrieves the user’s roles based on their ID.
6. Login and Logout: The component defines methods to initiate login and logout processes
using MSAL.
7. User Information: The loadUserFromLocalStorage() method retrieves the user's ID and name
from local storage to update the UI.
Step 2: Create an AuthService to Get User Roles, checks has role, checks has screen
access
Flow Summary
4. Role Check: RoleGuard checks if the user’s roles match the required roles.
Dependency Injection (DI) is a design pattern in Angular that provides a way to supply a class with its
dependencies without the class having to create them itself.
Example: DI in Angular
Let's create a simple service to understand how DI works in Angular.
1. Create a Service
Use the Angular CLI to generate a service:
ng generate service greeting
greeting.service.ts:
import { Injectable } from '@angular/core';
app.component.ts:
import { Component } from '@angular/core';
import { GreetingService } from './greeting.service';
@Component({
selector: 'app-root',
template: `<h1>{{ message }}</h1>`,
})
export class AppComponent {
message: string = '';
providers are used to inject dependencies into components, services, or other parts of the
application
App.module.ts
@NgModule({
providers: [
{ provide: 'BASE_API_URL', useValue: 'https://api.myapp.com' },
],
})
export class AppModule {}
Usage in a Service:
@Injectable({
providedIn: 'root',
})
export class DataService {
constructor(@Inject('BASE_API_URL') private baseUrl: string, private http: HttpClient) {}
getUsers() {
return this.http.get(`${this.baseUrl}/users`);
}
}
Factory Provider
A Factory Provider in Angular is used when you need to dynamically create or calculate a value based
on logic and can be used across application
Environment files
Angular.json file
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
api-factory.ts
app.module.ts
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [
{ provide: 'API_URL', useFactory: apiEndpointFactory }, // Register the factory provider
],
bootstrap: [AppComponent],
})
export class AppModule {}
data.service.ts
@Injectable({
providedIn: 'root',
})
export class DataService {
constructor(@Inject('API_URL') private apiUrl: string, private http: HttpClient) {}
getUsers() {
return this.http.get(`${this.apiUrl}/users`); // Use the dynamically injected API URL
}
}
This approach ensures that your application uses the correct API endpoint based on the build
environment, keeping things flexible and maintainable
Existing Providers
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
GridModule // Add the Kendo Grid module to your app
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
@Injectable({
providedIn: 'root'
})
export class ApiService {
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
api1Data: any;
api2Data: any;
ngOnInit(): void {
this.makeApiCalls();
}
makeApiCalls(): void {
forkJoin([
]).subscribe({
this.api1Data = api1Response;
this.api2Data = api2Response;
this.loading = false;
},
this.loading = false;
});
20) Rxjs
It provides a way to work with asynchronous data streams and enables a more declarative approach
to handle events, AJAX requests, and data transformations
Observer - An Observer is an object or a function that receives and handles/reacts to data emitted
by an Observable.
Analogy –
Observer - DJ
Subscriber – we listeners
Real time example of observable and observer - sending bulk data in batches
// Import necessary Angular and RxJS modules for creating a service and handling HTTP requests and
Observables.
@Injectable({
providedIn: 'root',
})
export class BatchService {
// The BatchService is a singleton service available throughout the app.
3. Handling Results:
o The Observer receives results (next), handles errors for each batch and marks the
process as complete once all batches are complete.
In essence, the Observable represents the asynchronous batch upload process, and the Observer
reacts to its outcomes.
21.2) Promise
A Promise is an object in JavaScript that represents the eventual completion or failure of an
asynchronous operation.
Analogy:
o The Reject is if the restaurant says they ran out of food (failure).
function fetchData() {
return new Promise((resolve, reject) => {
// Simulate a network request with a timeout
setTimeout(() => {
const success = true; // Change this to false to simulate a failure
if (success) {
resolve("Data fetched successfully!"); // The task was successful
} else {
reject("Error fetching data."); // The task failed
}
}, 2000); // Simulates a 2-second delay
});
}
2. Creation operators
of:
Creates an observable that emits a fixed number of values.
from:
Converts an array, promise, or iterable into an observable.
concat:
Concatenates multiple observables sequentially.
merge:
Merges multiple observables concurrently.
4. Transformation Operators
map:
Applies a function to each emitted value.
5. Filtering Operators
filter:
Filters values based on a condition.
import { of } from 'rxjs';
@Component({
selector: 'app-pipe-example',
template: `<div>{{ result }}</div>`
})
export class PipeExampleComponent implements OnInit {
result: number;
ngOnInit() {
// Create an observable with an array of numbers
const numbers$: Observable<number[]> = of([1, 2, 3, 4, 5]);
numbers$.pipe(
// First operator: filter out even numbers
filter(numbers => numbers.some(num => num % 2 === 0)),
// Second operator: multiply each number by 2
map(numbers => numbers.map(num => num * 2))
).subscribe(filteredNumbers => {
this.result = filteredNumbers[0]; // Display the first transformed number
});
}
}
tap is commonly used for debugging, logging, or triggering actions but doesn't alter the
stream.
Difference between tap and console.log is - tap is used in rxjs observable pipeline,
whereas consol.log can be used anywhere in the code
@Component({
selector: 'app-tap-example',
template: `<div>{{ result }}</div>`
})
export class TapExampleComponent implements OnInit {
result: number;
ngOnInit() {
// Create an observable with an array of numbers
const numbers$: Observable<number[]> = of([1, 2, 3, 4, 5]);
numbers$.pipe(
// `tap` is used for logging the numbers without altering the stream
tap(numbers => console.log('Before transformation:', numbers)),
// `map` is used to transform the numbers by doubling them
map(numbers => numbers.map(num => num * 2)),
// `tap` can be used again to log after transformation
tap(numbers => console.log('After transformation:', numbers))
).subscribe(transformedNumbers => {
this.result = transformedNumbers[0]; // Display the first transformed number
});
}
}
Analogy
Observable - coffee
Pipe – coffe making process
Tap- coffee tasting
7. Debouncing
├── app/
│ ├── core/ # Core module (singleton services, guards, interceptors)
│ │ └── ...
│ │ │ ├── button.component.ts
│ │ │ ├── button.component.html
│ │ │ └── button.component.css
│ │ ├── pipes/
│ │ │ └── ...
│ │ └── ...
│ │ │ ├── employee-list.component.ts
│ │ │ ├── employee-list.component.html
│ │ │ └── employee-list.component.css
│ │ └── ...
│ │ └── ...
│ │ └── ...
│ └── ...
@NgModule({
declarations: [
AppComponent, // Root component
],
imports: [
BrowserModule, // Required for any Angular app
AppRoutingModule, // App-level routing (manages navigation across the app)
CoreModule, // Core module for global services (e.g., Auth, HTTP
interceptors)
SharedModule, // Shared module for reusable components (e.g., buttons,
tables, etc.)
EmployeeModule, // Feature module for Employee management
CustomerModule, // Feature module for Customer management
OrderModule, // Feature module for Order management
],
providers: [], // Providers for app-wide services (typically in CoreModule)
bootstrap: [AppComponent] // Bootstrapping the app with the root component
})
export class AppModule {}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
Core Module
The Core module should contain services, guards, interceptors, and other singleton objects that are
used across the entire application. These should only be imported into the AppModule.
Services (e.g., authentication, logging, HTTP interceptors)
core.module.ts:
@NgModule({
providers: [
AuthService,
LoggingService,
AuthGuard,
HTTP_INTERCEPTORS,
ErrorInterceptor,
],
imports: [CommonModule]
})
Shared Module
The Shared module is where you place reusable components, directives, pipes, and other logic that
could be shared across multiple feature modules. For instance, buttons, cards, modals, etc.
shared.module.ts:
@NgModule({
imports: [CommonModule],
})
Feature Modules
For each screen (Employee, Customer, Order), create a feature module. Each feature module should
be responsible for managing its own components, services, and routing.
Services: Employee data service to fetch employee data from the backend
@NgModule({
providers: [EmployeeService],
})
Basic Example
HTML (app.component.html):
<h1>{{ counter }}</h1>
<button (click)="increment()">Increment</button>
Component (app.component.ts):
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
counter = 0;
increment() {
this.counter++;
}
}
When you click the button, the increment method is called, updating the counter. Angular
automatically detects this change and updates the <h1> element.
@Component({
selector: 'app-root',
template: `
<app-child [counter]="counter"></app-child>
<button (click)="increment()">Increment</button>
`,
styleUrls: ['./app.component.css'],
})
export class AppComponent {
counter = 0;
increment() {
this.counter++;
}
}
Child Component (child.component.ts):
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-child',
template: '<h1>{{ counter }}</h1>',
changeDetection: ChangeDetectionStrategy.OnPush, // Enable OnPush strategy
})
export class ChildComponent {
@Input() counter!: number;
}
Here, the child component will only detect changes if:
The counter value is passed as a new reference (e.g., a new object or primitive value).
Only for input property change change is detected and view is updated, rest all other
changes in both parent and child component change detections is skipped. This improves
performance of application
@Component({
selector: 'app-root',
template: `<h1>{{ message }}</h1>`,
styleUrls: ['./app.component.css'],
})
export class AppComponent {
message = 'Hello, Angular!';
Summary
Default Strategy: Angular automatically detects all changes in the component tree.
OnPush Strategy: Angular only checks changes for specific inputs or manual triggers,
improving performance.
Manual Detection: Use ChangeDetectorRef when Angular's default mechanism is not
sufficient.
Important Note-
In Angular, the root component (AppComponent) is at the top of the component tree, and
any changes in it can trigger change detection in the entire application to check its child
component. This is the default behaviour of change detection. To overcome this, we use
ChangeDetectionStrategy.OnPush.
Hence, any change in the parent component will trigger change detection for all its child
components
In Angular, change detection flows downwards from the parent to its child components, not
the other way around.
@Component({
selector: 'app-advanced-reactive-form',
template: `
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
<div>
<label>First Name:</label>
<input formControlName="firstName" />
<div *ngIf="firstName.invalid && firstName.touched">First Name is
required.</div>
<p *ngIf="firstName.dirty">First Name field is dirty (modified).</p>
<p *ngIf="firstName.pristine">First Name field is pristine (unchanged).</p>
</div>
<div>
<label>Email:</label>
<input formControlName="email" />
<div *ngIf="email.invalid && email.touched">Invalid Email Address.</div>
</div>
<div>
<label>Age:</label>
<input formControlName="age" type="number" />
<div *ngIf="age.invalid && age.touched">Age must be between 18 and
60.</div>
</div>
<div formArrayName="skills">
<label>Skills:</label>
<div *ngFor="let skill of skills.controls; let i = index">
<input [formControlName]="i" placeholder="Skill {{ i + 1 }}" />
</div>
<button type="button" (click)="addSkill()">Add Skill</button>
</div>
ngOnInit(): void {
// **valueChanges**: Emits an observable whenever the form values change
this.userForm.valueChanges.subscribe((value) => {
console.log('Form Value Changed:', value); // Logs the current value of the form
});
// Getters for easier access to controls, accessed by valid, invlaid, touched, dirty,
pristine
get firstName(): AbstractControl {
return this.userForm.get('firstName')!;
}
// **setValue**: Updates the entire form with exact values for each field
setInitialValues() {
this.userForm.setValue({
firstName: 'John',
email: '[email protected]',
age: 30,
skills: ['Angular', 'React'], // All form controls must be provided
});
console.log('Form values set using setValue:', this.userForm.value);
}
@Injectable()
export class GlobalErrorHandlerService implements ErrorHandler {
handleError(error: any): void {
// Log the error (to console or a logging service)
console.error('Global Error Caught:', error);
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [
{
provide: ErrorHandler, // Replace the default ErrorHandler
useClass: GlobalErrorHandlerService, // Use the custom
GlobalErrorHandlerService
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
@Component({
selector: 'app-root',
template: `
<h1>Global Exception Handling in Angular</h1>
<p *ngIf="data">Data: {{ data }}</p>
<p *ngIf="errorMessage" style="color: red;">Error: {{ errorMessage }}</p>
`,
})
export class AppComponent implements OnInit {
data: any;
errorMessage: string | null = null;
ngOnInit(): void {
// Simulate an API call
this.getDataFromApi();
}
getDataFromApi(): void {
// Intentionally use an invalid API URL to trigger an error
this.http.get('https://invalid-api-url.com/data').subscribe({
next: (response) => {
this.data = response;
},
error: (error) => {
console.error('Local Error Caught:', error);
this.errorMessage = 'Failed to load data!';
throw error; // Re-throw to trigger the global error handler, it is optional to use
},
});
}
}
26) Http Protocols
HTTP (HyperText Transfer Protocol):
o Data is sent over the network in plain text.
o It is not secure, which means the data being transferred can be intercepted or
modified by attackers.
HTTPS (HyperText Transfer Protocol Secure):
o Data is encrypted using TLS/SSL (Transport Layer Security/Secure Sockets Layer).
o It ensures secure communication between the client and the serve
<router-outlet></router-outlet>
@Injectable({
providedIn: 'root'
})
export class AuthService {
private user = ''; // Private variable
getUser() {
return () => this.user; // Closure that retains access to `user`
}
setUser(username: string) {
this.user = username;
}
}
app.component.ts
import { Component } from '@angular/core';
import { AuthService } from './auth.service';
@Component({
selector: 'app-root',
template: `
<button (click)="login()">Login</button>
<p>User: {{ getUser() }}</p>
`
})
export class AppComponent {
getUser: () => string;
login() {
this.authService.setUser('JohnDoe');
}
}
How It Works?
The AuthService method getUser() returns a closure that retains access to user.
Even when AppComponent assigns getUser, the function remembers the user variable.
✅ Closures help manage and encapsulate state!
Api calls
ngOnInit() {
console.log("Fetching data...");
this.dataService.getData().subscribe(response => {
console.log("Data received:", response);
});
console.log("Request sent, waiting for response...");
O/P
Fetching data...
Request sent, waiting for response...
Data received: [ { id: 1, title: "Post 1" }, { id: 2, title: "Post 2" }, ... ]
setTimeout
console.log("Step 1: Start");
setTimeout(() => {
console.log("Step 2: Processing (Delayed)");
}, 2000); // Simulates delay of 2 seconds
console.log("Step 3: End");
O/P
Step 1: Start
Step 3: End
Step 2: Processing (Delayed) // This appears after 2 seconds
Synchronous:
Code executes line by line, one after another. The next line of code waits for the previous one to
complete.
Example
function login(username: string, password: string): boolean {
console.log("Verifying user credentials...");
O/P
Step 1: User tries to login
Verifying user credentials...
Login successful!
Step 2: Redirect user based on login status
@Component({
selector: 'app-timeout-example',
template: '<p>{{ message }}</p>'
})
export class TimeoutExampleComponent implements OnInit {
message: string = "Waiting...";
ngOnInit() {
console.log("Step 1: Timer started...");
setTimeout(() => {
this.message = "Hello after 3 seconds!";
console.log("Step 2: Message updated!");
}, 3000); // 3 seconds delay
}
}
O/P
Step 1: Timer started...
(After 3 seconds)
Step 2: Message updated!
@Component({
selector: 'app-interval-example',
template: '<p>Current Time: {{ currentTime }}</p>'
})
export class IntervalExampleComponent implements OnInit, OnDestroy {
currentTime: string = new Date().toLocaleTimeString();
intervalId: any;
ngOnInit() {
console.log("Clock started...");
ngOnDestroy() {
clearInterval(this.intervalId); // Stops the interval when component is destroyed
console.log("Clock stopped!");
}
}
O/P
Clock started...
Updated time: 10:00:01 AM
Updated time: 10:00:02 AM
Updated time: 10:00:03 AM
(Updates every second)
setTimeout(() => {
console.log("Step 2: Pizza is ready!");
callback("Enjoy your pizza! 🍕"); // Calling the callback function
}, 3000); // Simulating 3 seconds pizza preparation time
}
O/P
Step 1: Order placed. Preparing pizza...
(After 3 seconds)
Step 2: Pizza is ready!
Step 3: Enjoy your pizza! 🍕
39) Async await in angular
Asynchronous call –
ngOnInit() {
console.log("Fetching data...");
this.dataService.getData().subscribe(response => {
console.log("Data received:", response);
});
}
Here, api call is asynchronous, as it don’t block the further execution, but this is some times not
beneficial.
Hence, this can be overcome by converting above code to synchronous using async, await and
lastValueFrom() [converts observable to promise]
synchronous call –
@Injectable({
providedIn: 'root'
})
export class DataService {
private apiUrl = 'https://jsonplaceholder.typicode.com/posts/1'; // Dummy API
getData(): Observable<any> {
return this.http.get<any>(this.apiUrl);
}
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
constructor(private dataService: DataService) {}
async ngOnInit() {
console.log("Fetching data...");
try {
const response = await lastValueFrom(this.dataService.getData());
console.log("Data received:", response);
} catch (error) {
console.error("Error fetching data:", error);
}
console.log("Request completed.");
}
}
Expected Output
📌 What you will see in the browser
Single-Slot Projection Example
--------------------------------
Child Component
This content comes from the parent.
2. Projecting Components Inside <ng-content>
👉 You can pass Angular components inside <ng-content>.
Other Component (other.component.html)
<div class="other-box">
<p>This is another component!</p>
</div>
Expected Output
📌 What you will see in the browser
Component Projection Example
--------------------------------
Child Component
This is another component!
<div class="modal-body">
<ng-content select=".modal-body"></ng-content> <!-- For Body -->
</div>
<div class="modal-footer">
<ng-content select=".modal-footer"></ng-content> <!-- For Footer -->
</div>
</div>
Select attribute is mainly used for custome class having css code
Add Some Basic Styling for the Modal:
/* modal.component.css */
.modal {
background: white;
border-radius: 5px;
padding: 20px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
}
.modal-header {
font-size: 20px;
font-weight: bold;
margin-bottom: 10px;
}
.modal-body {
font-size: 16px;
margin-bottom: 20px;
}
.modal-footer {
text-align: right;
}
<app-modal>
<div class="modal-body">Here is the main content of the modal. You can put any message or form
here.</div> <!-- Body content -->
<div class="modal-footer">
<button (click)="confirmAction()">OK</button>
</div>
</app-modal>
<app-modal>
<div class="modal-body">Something went wrong. Please try again later.</div> <!-- Body content -->
<div class="modal-footer">
</div>
</app-modal>