Angular Complete Notes
Angular Complete Notes
Key Features
• Component-based architecture
• Two-way data binding
• Dependency Injection (DI)
• TypeScript-first approach
• Powerful CLI tooling
• Built-in routing, forms, HTTP client
• RxJS for reactive programming
Installation
# Install Angular CLI globally
npm install -g @angular/cli
# Create a new project
ng new my-app
# Navigate to project
cd my-app
Project Structure
my-app/
├── src/
│ ├── app/
│ │ ├── [Link] ← Root component
│ │ ├── [Link] ← Template
│ │ ├── [Link] ← Styles
│ │ └── [Link] ← Root module
│ ├── assets/ ← Static files
│ ├── environments/ ← Env configs
│ └── [Link] ← Entry point
├── [Link] ← CLI config
├── [Link]
└── [Link]
Part 2: Components
2.1 What is a Component?
A component is the basic building block of an Angular application. Each component controls a
patch of screen called a view. Components consist of three parts: a TypeScript class, an HTML
template, and optional CSS styles.
Component Anatomy
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-my-component', // HTML tag name
templateUrl: './[Link]', // External template
styleUrls: ['./[Link]'] // Styles
// OR inline:
// template: `<h1>Hello</h1>`,
// styles: [`h1 { color: blue; }`]
})
export class MyComponent implements OnInit {
title: string = 'Hello Angular';
count: number = 0;
constructor() { }
ngOnInit(): void {
// Runs once on component load
}
increment(): void {
[Link]++;
}
}
2.3 Data Binding
Interpolation (Component → Template)
// Component
name = 'Angular';
// Template
<h1>Hello {{ name }}</h1>
<p>2 + 2 = {{ 2 + 2 }}</p>
Property Binding
// Component
imageUrl = '[Link]
isDisabled = true;
// Template
<img [src]="imageUrl">
<button [disabled]="isDisabled">Click</button>
Event Binding
// Component
onClick() { [Link]('Clicked!'); }
// Template
<button (click)="onClick()">Click me</button>
<input (keyup)="onKeyUp($event)">
// Component
username = '';
// Template
<input [(ngModel)]="username">
<p>Hello, {{ username }}</p>
Note: Two-way binding uses the 'banana in a box' syntax: [( )] = property binding + event binding
combined.
2.4 Component Communication
Parent to Child — @Input()
// Child component
import { Input } from '@angular/core';
// Parent template
<app-child [title]="'Hello'" [count]="myCount"></app-child>
sendMessage() {
[Link]('Hello from child!');
}
}
// Parent template
<app-child (messageSent)="onMessage($event)"></app-child>
// Parent component
onMessage(msg: string) { [Link](msg); }
<ng-template #loginBlock>
<p>Please log in.</p>
</ng-template>
*ngFor
// Component
users = ['Alice', 'Bob', 'Charlie'];
// Template
<ul>
<li *ngFor="let user of users; let i = index">
{{ i + 1 }}. {{ user }}
</li>
</ul>
// Component
trackById(index: number, item: any) { return [Link]; }
*ngSwitch
<div [ngSwitch]="color">
<p *ngSwitchCase="'red'">Red selected</p>
<p *ngSwitchCase="'blue'">Blue selected</p>
<p *ngSwitchDefault>Unknown color</p>
</div>
ngStyle
<div [ngStyle]="{'color': textColor, 'font-size': fontSize + 'px'}">
Styled text
</div>
@HostListener('mouseenter')
onMouseEnter() {
[Link] = [Link];
}
@HostListener('mouseleave')
onMouseLeave() {
[Link] = '';
}
}
// Usage:
<p appHighlight="lightblue">Hover over me</p>
3.4 Pipes
Built-in Pipes
{{ name | uppercase }} → ANGULAR
{{ name | lowercase }} → angular
{{ price | currency:'INR' }} → ₹1,000.00
{{ date | date:'dd/MM/yyyy' }} → 18/03/2026
{{ ratio | percent }} → 75%
{{ object | json }} → { 'key': 'value' }
{{ 'hello world' | titlecase }} → Hello World
{{ items | sli[Link] }} → first 3 items
Custom Pipe
// ng g p truncate
// Usage:
<p>{{ longText | truncate:100 }}</p>
Part 4: Modules & Dependency Injection
4.1 NgModules
NgModules organize an application into cohesive blocks of functionality. Every Angular app has
at least one module — the root module (AppModule).
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './[Link]';
import { AppComponent } from './[Link]';
@NgModule({
declarations: [ // Components, directives, pipes belonging to this module
AppComponent
],
imports: [ // Other modules this module needs
BrowserModule,
AppRoutingModule
],
providers: [], // Services available to this module
bootstrap: [AppComponent] // Root component to launch
})
export class AppModule { }
@Injectable({
providedIn: 'root' // Singleton — available app-wide
})
export class UserService {
private users: string[] = ['Alice', 'Bob'];
getUsers(): string[] {
return [Link];
}
Best Practice: Always use providedIn: 'root' for services you want as app-wide singletons. This also
enables tree-shaking.
Part 5: Routing & Navigation
5.1 Setting Up Routing
// [Link]
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/[Link]';
import { AboutComponent } from './about/[Link]';
import { NotFoundComponent } from './not-found/[Link]';
@NgModule({
imports: [[Link](routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
onLogin() {
// Navigate after login
[Link](['/dashboard']);
// With query params:
[Link](['/search'], { queryParams: { q: 'angular' } });
}
}
canActivate(): boolean {
const isLoggedIn = !;
if (!isLoggedIn) [Link](['/login']);
return isLoggedIn;
}
}
// Apply in routes:
{ path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] }
5.5 Lazy Loading
// Lazy load a feature module
const routes: Routes = [
{
path: 'admin',
loadChildren: () =>
import('./admin/[Link]').then(m => [Link])
}
];
// Template
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">
<input
name="email"
[(ngModel)]="email"
required email
#emailField="ngModel"
/>
<div *ngIf="[Link] && [Link]">
Invalid email
</div>
<button type="submit" [disabled]="[Link]">Submit</button>
</form>
// Component
email = '';
onSubmit(form: NgForm) {
[Link]([Link]);
}
// Component
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
onSubmit() {
if ([Link]) {
[Link]([Link]);
}
}
}
// Template
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<input formControlName="email" />
<div *ngIf="email?.invalid && email?.touched">
<span *ngIf="email?.errors?.['required']">Email required</span>
<span *ngIf="email?.errors?.['email']">Invalid email</span>
</div>
<input type="password" formControlName="password" />
<button type="submit" [disabled]="[Link]">Login</button>
</form>
// Usage:
username: ['', [[Link], noSpaceValidator()]]
Part 7: HTTP Client & APIs
7.1 Setting Up HttpClient
// [Link]
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [HttpClientModule]
})
interface Post {
id: number;
title: string;
body: string;
}
getPosts(): Observable<Post[]> {
return [Link]<Post[]>([Link]);
}
// In component:
ngOnInit() {
[Link]().subscribe({
next: (posts) => [Link] = posts,
error: (err) => [Link](err),
complete: () => [Link]('Done')
});
}
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
const token = [Link]('token');
const cloned = [Link]({
headers: [Link]('Authorization', `Bearer ${token}`)
});
return [Link](cloned);
}
}
updateCount(n: number) {
[Link](n);
}
}
// Template
<div *ngIf="posts$ | async as posts">
<div *ngFor="let post of posts">
{{ [Link] }}
</div>
</div>
Best Practice: Use the async pipe in templates instead of subscribing manually — it handles
unsubscription automatically, preventing memory leaks.
Part 9: Advanced Angular
9.1 Change Detection
Angular checks the whole component tree by default (Default strategy). For performance-critical
apps, use OnPush to only check when @Input references change.
import { ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
@Component({
selector: 'app-card',
changeDetection: [Link]
})
export class CardComponent {
@Input() data!: any;
// Usage
<app-card>
<h2 card-title>My Title</h2>
<p>Card body content</p>
</app-card>
ngAfterViewInit() {
[Link]();
[Link]();
}
}
// Template
<input #myInput placeholder="Focus me" />
@Component({
selector: 'app-home',
standalone: true,
imports: [CommonModule, RouterModule], // Import directly!
template: `<h1>Home Page</h1>`
})
export class HomeComponent { }
constructor() {
effect(() => { // Side effect
[Link]('Count changed:', [Link]());
});
}
// Template
<p>Count: {{ count() }}</p>
<p>Doubled: {{ doubled() }}</p>
<button (click)="increment()">+</button>
Part 10: State Management with NgRx
10.1 NgRx Core Concepts
• Store — single source of truth (immutable state)
• Actions — events describing what happened
• Reducers — pure functions that update state
• Selectors — queries to extract data from state
• Effects — handle async side effects (HTTP calls)
// reducers/[Link]
import { createReducer, on } from '@ngrx/store';
// selectors/[Link]
import { createSelector, createFeatureSelector } from '@ngrx/store';
// In component
export class CounterComponent {
count$ = [Link](selectCount);
increment() { [Link](increment()); }
decrement() { [Link](decrement()); }
}
Part 11: Testing in Angular
11.1 Unit Testing with Jasmine & Karma
// [Link]
import { TestBed } from '@angular/core/testing';
import { CounterService } from './[Link]';
describe('CounterService', () => {
let service: CounterService;
beforeEach(() => {
[Link]({});
service = [Link](CounterService);
});
describe('AppComponent', () => {
let fixture: ComponentFixture<AppComponent>;
let component: AppComponent;
beforeEach(async () => {
await [Link]({
declarations: [AppComponent]
}).compileComponents();
fixture = [Link](AppComponent);
component = [Link];
[Link]();
});
it('should render title', () => {
const compiled = [Link] as HTMLElement;
expect([Link]('h1')?.textContent).toContain('Hello');
});
});
// Run tests:
ng test
Part 12: Performance & Best Practices
12.1 Performance Tips
• Use OnPush change detection for components with immutable inputs
• Use trackBy with *ngFor to avoid re-rendering entire lists
• Lazy load feature modules to reduce initial bundle size
• Use the async pipe instead of manual subscriptions
• Unsubscribe from Observables in ngOnDestroy to prevent memory leaks
• Use pure pipes instead of methods in templates
• Avoid complex expressions in templates
ngOnInit() {
[Link]$.pipe(
takeUntil([Link]$)
).subscribe(data => [Link] = data);
}
ngOnDestroy() {
[Link]$.next();
[Link]$.complete();
}
}