Google Framework Β· Enterprise Scale Β· India 2025

Angular β€”
Zero to Interview
Ready

The complete Angular guide β€” from NgModules to Signals, from basic data binding to NgRx and zoneless change detection. Every topic asked in Indian interviews, from TCS & Infosys to Google, Flipkart, and Deutsche Bank tech teams.

πŸ…° Angular 17+
16 Modules
RxJS Deep Dive
NgRx & Signals
Startup β†’ Enterprise
Live Code Examples
Module 01 Β· Fundamentals
● Beginner

Angular Architecture

Angular is a complete platform by Google β€” not just a library. It includes its own router, HTTP client, forms, DI system, build tools, and testing utilities. Understanding its architecture first makes everything else click.

Angular vs React vs Vue
React: library (View only). Vue: progressive framework. Angular: full platform β€” batteries included. Angular is opinionated (one way to do things), making large team codebases consistent. Used heavily in enterprise India: TCS, Infosys, Wipro, Deutsche Bank, HDFC tech.
Full frameworkTypeScript first
Core Building Blocks
Components (UI), Templates (HTML), Services (logic/data), Modules (grouping), Directives (DOM manipulation), Pipes (data transformation), Guards (route protection), Interceptors (HTTP middleware). Everything has a clear role.
Structured
TypeScript Native
Angular is written IN TypeScript and expects TypeScript. Strong typing, decorators (@Component, @Injectable), interfaces, generics β€” all standard. This is what makes large Angular apps maintainable where JavaScript would become chaos.
Required
Angular CLI
ng new, ng generate component, ng serve, ng build, ng test. The CLI scaffolds, builds (Webpack/esbuild), runs tests, and lints. Dramatically faster development than manual setup. ng generate handles 90% of daily file creation.
Essential tool
🎯 Every Interview β€” TCS, Wipro, Infosys
What is Angular? How is it different from AngularJS?
Angular (2+) is a complete rewrite of AngularJS. Major differences: Angular uses TypeScript (AngularJS: JavaScript), Angular uses components (AngularJS: controllers + $scope), Angular has a unidirectional data flow (AngularJS: two-way by default everywhere), Angular has a proper CLI and module system, and Angular is significantly faster. They share a name but are completely different frameworks.
Module 02 Β· Fundamentals
● Beginner

Components & Templates

Components are the fundamental UI building blocks. Each consists of a TypeScript class, an HTML template, and optional CSS. The @Component decorator connects them and defines how the component appears in HTML.

02_component.ts + template.html
angular
// ─── Component decorator β€” connects everything ────────────
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';

@Component({
  selector: 'app-user-card',        // <app-user-card> in templates
  templateUrl: './user-card.html',   // separate HTML file
  styleUrls: ['./user-card.css'],    // component-scoped CSS
  standalone: true,                  // Angular 14+ β€” no NgModule needed
})
export class UserCardComponent implements OnInit {
  // @Input β€” receive data FROM parent
  @Input() name!: string;             // ! = definitely assigned (TypeScript)
  @Input() age = 0;                  // default value
  @Input({ required: true }) id!: string; // Angular 16+ required input

  // @Output β€” emit events TO parent (EventEmitter)
  @Output() selected = new EventEmitter<string>();

  isVisible = true;

  ngOnInit(): void {
    console.log(`Component ready: ${this.name}`);
  }

  selectUser(): void {
    this.selected.emit(this.id);  // notify parent
  }
}

// ─── Template (user-card.html) ────────────────────────────
<!-- Property binding: [] binds to component property -->
<!-- Event binding: () listens to DOM events -->
<!-- Two-way binding: [()] = property + event combined -->
user-card.html β€” Template syntax
html
<!-- New Angular 17+ Control Flow Syntax (replaces *ngIf, *ngFor) -->
@if (isVisible) {
  <div class="card">
    <h2>{{ name }}</h2>                     <!-- interpolation -->
    <p [class.adult]="age >= 18">{{age}}</p>  <!-- class binding -->
    <button (click)="selectUser()">Select</button> <!-- event binding -->
  </div>
}

@for (item of items; track item.id) {   <!-- track = key prop equivalent -->
  <app-item [data]="item" />
}

@switch (status) {
  @case ('active') { <span>Active</span> }
  @case ('idle') { <span>Idle</span> }
  @default { <span>Unknown</span> }
}

<!-- OLD way (still works, know both) -->
<div *ngIf="isVisible">...</div>
<li *ngFor="let item of items; trackBy: trackById">...</li>
Module 03 Β· Fundamentals
● Beginner

Data Binding & Directives

Binding TypeSyntaxDirectionExample
Interpolation{{ expression }}Component β†’ DOM<p>{{ user.name }}</p>
Property binding[property]="value"Component β†’ DOM[src]="imageUrl"
Event binding(event)="handler()"DOM β†’ Component(click)="submit()"
Two-way binding[(ngModel)]="prop"Both ways[(ngModel)]="name"
Attribute binding[attr.aria-label]="label"Component β†’ DOM attr[attr.colspan]="span"
Class binding[class.active]="isActive"Component β†’ CSS class[class.error]="hasError"
Style binding[style.color]="color"Component β†’ CSS style[style.fontSize.px]="size"
03_directives.ts
typescript
// ─── Structural Directives β€” change DOM structure ─────────
// *ngIf, *ngFor, *ngSwitch (old syntax)
// @if, @for, @switch (Angular 17+ new syntax β€” preferred)

// ─── Attribute Directives β€” change element appearance ─────
// Built-in: ngClass, ngStyle
<div [ngClass]="{ 'active': isActive, 'error': hasError }"></div>
<div [ngStyle]="{ 'color': textColor, 'font-size': '14px' }"></div>

// ─── Custom Directive ─────────────────────────────────────
@Directive({ selector: '[appHighlight]', standalone: true })
export class HighlightDirective {
  @Input() appHighlight = 'yellow';   // directive input = selector name

  constructor(private el: ElementRef) {}

  @HostListener('mouseenter')
  onMouseEnter() { this.el.nativeElement.style.background = this.appHighlight; }

  @HostListener('mouseleave')
  onMouseLeave() { this.el.nativeElement.style.background = ''; }
}
// Usage: <p appHighlight="cyan">Hover me</p>
🎯 Very common β€” all levels
Difference between structural and attribute directives?
Structural directives change the DOM structure β€” they add or remove elements. They use the * prefix (*ngIf, *ngFor) or the new @if/@for syntax. Attribute directives change the appearance or behavior of an existing element without adding/removing it β€” they modify attributes, classes, styles (ngClass, ngStyle, and custom directives). Both extend HTML β€” structural ones reshape the DOM tree, attribute ones modify existing nodes.
Module 04 Β· Fundamentals
● Beginner β†’ Mid

NgModules & Standalone Components

Angular originally required NgModules to organize the app. Angular 14+ introduced Standalone Components β€” the modern approach. Both exist in codebases, so you must know both.

04_modules_standalone.ts
typescript
// ─── NgModule approach (classic β€” still in most codebases) ─
@NgModule({
  declarations: [AppComponent, UserCardComponent], // components/directives/pipes
  imports: [BrowserModule, RouterModule, HttpClientModule],
  providers: [UserService],
  bootstrap: [AppComponent],                        // root component
})
export class AppModule {}

// ─── Standalone approach (Angular 14+ β€” modern, preferred) ─
@Component({
  selector: 'app-root',
  standalone: true,                              // no NgModule needed
  imports: [RouterOutlet, UserCardComponent, AsyncPipe], // import directly
  template: `<router-outlet />`,
})
export class AppComponent {}

// ─── Bootstrap standalone app (main.ts) ───────────────────
bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes),
    provideHttpClient(),
    provideAnimations(),
  ]
});
// No AppModule! Providers registered directly.
πŸ’‘ Interview Context β€” 2025

Most new Angular projects use Standalone Components. Legacy codebases still use NgModules. Angular's official recommendation since Angular 17 is to use standalone. If asked "NgModule vs Standalone" β€” standalone is simpler, no module overhead, better tree-shaking, aligns with the future direction of Angular.

Module 05 Β· Fundamentals
β—† Mid Level

Dependency Injection

Angular's DI system is one of its most powerful features β€” and the most asked about in senior interviews. Services are registered as providers and injected where needed. Understanding injection hierarchies is critical.

05_dependency_injection.ts
typescript
// ─── Basic service with inject() (modern, preferred) ──────
@Injectable({ providedIn: 'root' })   // singleton across entire app
export class UserService {
  private http = inject(HttpClient);   // inject() β€” Angular 14+ modern way
  private router = inject(Router);

  getUsers() {
    return this.http.get<User[]>('/api/users');
  }
}

// ─── Old constructor injection (still valid) ───────────────
@Component({...})
export class AppComponent {
  constructor(private userService: UserService) {}  // constructor injection
}

// ─── Injection hierarchies ────────────────────────────────
// providedIn: 'root'   β†’ ONE instance for entire app
// providers: [MyService] in component β†’ new instance for that component subtree
// providers: [MyService] in module β†’ shared within that module

// ─── InjectionToken β€” for non-class values ─────────────────
const API_URL = new InjectionToken<string>('API_URL');
// In providers: { provide: API_URL, useValue: 'https://api.example.com' }
const apiUrl = inject(API_URL);  // inject in component/service

// ─── Provider types ───────────────────────────────────────
// useClass:   { provide: Logger, useClass: ConsoleLogger }  β€” substitute class
// useValue:   { provide: API_URL, useValue: 'https://...' } β€” literal value
// useFactory: { provide: X, useFactory: () => new X() }     β€” factory function
// useExisting: { provide: A, useExisting: B }               β€” alias
🎯 Senior β€” Deutsche Bank, Accenture, Capgemini
Explain Angular's DI hierarchy. What happens if a service is provided at component level vs root?
providedIn: 'root' creates a singleton β€” ONE instance shared across the whole app. Every component/service gets the same instance. Providing in a component's providers: [] creates a NEW instance for that component and all its children, isolated from the rest. Use component-level when you want separate state per component instance (like a form service that tracks one form's state). The DI system checks the hierarchy: component injector β†’ parent component β†’ root injector.
Module 06 Β· Core
● Beginner β†’ Mid

Lifecycle Hooks

Angular calls lifecycle hook methods at specific moments. Understanding the ORDER they fire and WHAT to do in each is a universal interview question.

HookWhen it firesUse for
ngOnChangesBefore ngOnInit AND when @Input changesReact to input changes, use SimpleChanges to compare
ngOnInitOnce after first ngOnChanges (component initialized)Fetch data, initialize subscriptions, setup
ngDoCheckEvery change detection cycleCustom change detection (rarely use this)
ngAfterContentInitOnce after <ng-content> projectedAccess projected content via @ContentChild
ngAfterViewInitOnce after component view + children initializedAccess DOM elements via @ViewChild
ngAfterViewCheckedAfter every change detection of viewAfter view updates (careful β€” runs often)
ngOnDestroyJust before component removedUnsubscribe observables, cleanup timers, remove listeners
06_lifecycle.ts β€” Critical patterns
typescript
export class MyComponent implements OnInit, OnDestroy, OnChanges {
  @Input() userId!: string;
  @ViewChild('myCanvas') canvas!: ElementRef;  // available in ngAfterViewInit

  private destroy$ = new Subject<void>();        // for unsubscribing
  private userService = inject(UserService);

  ngOnChanges(changes: SimpleChanges) {
    if (changes['userId']) {                       // input changed
      this.loadUser(changes['userId'].currentValue);
    }
  }

  ngOnInit() {
    this.userService.getUser(this.userId)
      .pipe(takeUntil(this.destroy$))              // auto-unsubscribe!
      .subscribe(user => this.user = user);
  }

  ngAfterViewInit() {
    const ctx = this.canvas.nativeElement.getContext('2d');  // DOM ready
  }

  ngOnDestroy() {
    this.destroy$.next();   // complete all takeUntil subscriptions
    this.destroy$.complete();
  }
}
⚠️ Memory Leak Warning

NOT unsubscribing from Observables in ngOnDestroy is the #1 source of memory leaks in Angular apps. Always use takeUntil(destroy$), the async pipe (auto-unsubscribes), or the DestroyRef inject pattern (Angular 16+): inject(DestroyRef).onDestroy(() => sub.unsubscribe()).

Module 07 Β· Core
β—† Mid Level

Services & HTTP Client

07_service_http.ts
typescript
import { inject, Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
import { Observable, catchError, map, retry, throwError } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class ProductService {
  private http = inject(HttpClient);
  private baseUrl = 'https://api.example.com';

  // GET with params
  getProducts(page = 1, limit = 20): Observable<Product[]> {
    const params = new HttpParams().set('page', page).set('limit', limit);
    return this.http.get<Product[]>(`${this.baseUrl}/products`, { params }).pipe(
      retry(2),                             // retry twice on failure
      catchError(this.handleError)           // centralized error handling
    );
  }

  // POST with typed body
  createProduct(product: Omit<Product, 'id'>): Observable<Product> {
    return this.http.post<Product>(`${this.baseUrl}/products`, product);
  }

  private handleError(err: HttpErrorResponse) {
    const msg = err.error?.message ?? err.statusText;
    return throwError(() => new Error(msg));
  }
}

// ─── HTTP Interceptor (Angular 15+) ───────────────────────
export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const token = localStorage.getItem('token');
  const authReq = token
    ? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })
    : req;
  return next(authReq);
};
// Register: provideHttpClient(withInterceptors([authInterceptor]))
Module 08 Β· Core
β—† Mid Level

Routing & Guards

08_routing.ts
typescript
// ─── Route config (standalone app) ───────────────────────
export const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'user/:id', component: UserComponent },
  // Lazy loading β€” module loaded on demand
  {
    path: 'admin',
    loadChildren: () => import('./admin/routes').then(m => m.adminRoutes),
    canActivate: [authGuard],              // protect route
  },
  // Lazy standalone component
  {
    path: 'dashboard',
    loadComponent: () => import('./dashboard.component').then(m => m.DashboardComponent),
  },
  { path: '**', component: NotFoundComponent },
];

// ─── Functional Guard (Angular 15+ preferred) ─────────────
export const authGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isLoggedIn()) return true;
  router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
  return false;
};

// ─── Router hooks in component ────────────────────────────
export class UserComponent {
  private route = inject(ActivatedRoute);
  private router = inject(Router);

  userId = this.route.snapshot.paramMap.get('id');      // static
  userId$ = this.route.paramMap.pipe(map(p => p.get('id')));  // reactive

  navigate() { this.router.navigate(['/users']); }
}
Module 09 Β· Core
β—† Mid Level

Forms β€” Template-Driven & Reactive

Angular has two form approaches. Template-driven for simple forms. Reactive for complex, dynamic, and testable forms. Reactive forms are preferred in modern Angular and asked more in interviews.

09_reactive_forms.ts
typescript
import { FormBuilder, Validators, AbstractControl } from '@angular/forms';

export class LoginComponent {
  private fb = inject(FormBuilder);

  form = this.fb.group({
    email: ['', [Validators.required, Validators.email]],
    password: ['', [Validators.required, Validators.minLength(8)]],
    age: [null, [Validators.min(18), Validators.max(100)]],
  });

  // Typed access (Angular 14+ β€” typed forms)
  get email() { return this.form.get('email')!; }

  onSubmit() {
    if (this.form.invalid) return;
    console.log(this.form.value);      // { email: '...', password: '...' }
  }

  // ─── Custom validator ─────────────────────────────────────
  noSpaces(control: AbstractControl) {
    return control.value?.includes(' ') ? { noSpaces: true } : null;
  }

  // ─── Cross-field validator (group level) ──────────────────
  passwordMatch(group: AbstractControl) {
    const pass = group.get('password')?.value;
    const confirm = group.get('confirm')?.value;
    return pass === confirm ? null : { mismatch: true };
  }

  // ─── Dynamic FormArray (add/remove items) ─────────────────
  addressForm = this.fb.group({
    addresses: this.fb.array([])
  });
  get addresses() { return this.addressForm.get('addresses') as FormArray; }
  addAddress() { this.addresses.push(this.fb.group({ street: '', city: '' })); }
}
🎯 Mid-Senior β€” All companies
Template-driven vs Reactive forms β€” when to use each?
Template-driven: form logic lives in HTML template, simpler setup, uses NgModel, good for simple forms (contact, login). Reactive: form logic lives in TypeScript class, more explicit control, better for complex/dynamic forms, easier to unit test, supports cross-field validation. Key difference: Reactive forms are synchronous (access control values immediately), template-driven are asynchronous (form built during change detection). Senior interviews prefer Reactive form knowledge.
Module 10 Β· RxJS & State
β—† Mid β†’ Senior

RxJS Deep Dive

RxJS is the backbone of Angular. Observables, operators, and subjects are asked heavily in mid-to-senior interviews. The flattening operators (switchMap, mergeMap, concatMap, exhaustMap) are the most important to master.

10_rxjs.ts β€” The operators that matter
typescript
import {
  Observable, Subject, BehaviorSubject, combineLatest, forkJoin, merge,
  switchMap, mergeMap, concatMap, exhaustMap,
  debounceTime, throttleTime, distinctUntilChanged,
  takeUntil, take, filter, map, catchError, retry, shareReplay
} from 'rxjs';

// ─── Subjects ─────────────────────────────────────────────
// Subject:          multicast, no initial value, only future values
// BehaviorSubject:  multicast, has initial value, new subscribers get CURRENT
// ReplaySubject(n): multicast, replays last n values to new subscribers
// AsyncSubject:     emits ONLY the last value WHEN COMPLETED

const user$ = new BehaviorSubject<User | null>(null);
user$.next(loggedInUser);         // push new value
user$.getValue();                 // synchronously get current value

// ─── THE MOST IMPORTANT OPERATORS ─────────────────────────
// switchMap  β€” cancels previous inner observable when new outer value arrives
//              BEST for: search autocomplete, navigation
searchInput$.pipe(
  debounceTime(300),               // wait 300ms after last keystroke
  distinctUntilChanged(),           // only if value actually changed
  switchMap(query => this.api.search(query))  // cancel old request!
);

// concatMap β€” waits for previous to complete before next
//             BEST for: sequential API calls, form submissions
clicks$.pipe(concatMap(() => this.api.save()));  // save one at a time

// mergeMap  β€” runs all in parallel, results in any order
//             BEST for: parallel requests with no order dependency
ids$.pipe(mergeMap(id => this.api.getItem(id)));  // all at once

// exhaustMap β€” ignores new outer values while inner is still active
//              BEST for: login button (ignore double-click)
loginClicks$.pipe(exhaustMap(() => this.auth.login(creds)));  // ignore extra clicks

// ─── Combining observables ────────────────────────────────
combineLatest([user$, settings$]).pipe(          // emits when ANY changes
  map(([user, settings]) => ({ ...user, ...settings }))
);
forkJoin([api.getUsers(), api.getProducts()]);  // wait for ALL to complete

// ─── shareReplay β€” multicast + cache for late subscribers ──
readonly user$ = this.http.get('/api/me').pipe(shareReplay(1));
// Multiple async pipes β†’ only ONE HTTP request!
🎯 Senior β€” Very heavily asked
When would you use switchMap vs concatMap vs mergeMap?
switchMap: when new value should cancel the previous operation β€” search (user types new query, cancel old request), routing. concatMap: when ORDER matters and operations must complete sequentially β€” upload files one by one, sequential state changes. mergeMap: when order doesn't matter and you want maximum parallelism β€” fetch details for multiple items simultaneously. exhaustMap: when you want to ignore new triggers while busy β€” login button, form submit (prevent double-submit). Mixing these up is a serious bug.
Module 11 Β· RxJS & State
β—ˆ Senior

NgRx State Management

NgRx is Redux for Angular β€” Actions, Reducers, Effects, Selectors. Used in large enterprise Angular apps. Senior interviews at product companies expect NgRx knowledge.

11_ngrx.ts β€” Complete pattern
typescript
import { createAction, createReducer, createEffect, createSelector,
         createFeatureSelector, props, on } from '@ngrx/store';
import { Actions, ofType } from '@ngrx/effects';

// ─── 1. Actions ───────────────────────────────────────────
export const loadUsers = createAction('[Users] Load Users');
export const loadUsersSuccess = createAction('[Users] Load Success',
  props<{ users: User[] }>());
export const loadUsersFailure = createAction('[Users] Load Failure',
  props<{ error: string }>());

// ─── 2. Reducer ───────────────────────────────────────────
const reducer = createReducer(
  { users: [] as User[], loading: false, error: null as string | null },
  on(loadUsers, state => ({ ...state, loading: true })),
  on(loadUsersSuccess, (state, { users }) => ({ ...state, loading: false, users })),
  on(loadUsersFailure, (state, { error }) => ({ ...state, loading: false, error })),
);

// ─── 3. Selectors ─────────────────────────────────────────
const selectUsersState = createFeatureSelector<UsersState>('users');
export const selectAllUsers = createSelector(selectUsersState, s => s.users);
export const selectLoading = createSelector(selectUsersState, s => s.loading);
// Selectors are MEMOIZED β€” only recompute when inputs change

// ─── 4. Effects (side effects) ────────────────────────────
@Injectable()
export class UsersEffects {
  private actions$ = inject(Actions);
  private userService = inject(UserService);

  loadUsers$ = createEffect(() =>
    this.actions$.pipe(
      ofType(loadUsers),
      switchMap(() => this.userService.getUsers().pipe(
        map(users => loadUsersSuccess({ users })),
        catchError(err => of(loadUsersFailure({ error: err.message })))
      ))
    )
  );
}

// ─── 5. In Component ──────────────────────────────────────
export class UsersComponent {
  private store = inject(Store);
  users$ = this.store.select(selectAllUsers);
  loading$ = this.store.select(selectLoading);

  loadUsers() { this.store.dispatch(loadUsers()); }
}
Module 12 Β· Signals β€” Angular 16+
β—ˆ Senior Β· Hot Topic 2025

Angular Signals

Signals are the most significant Angular feature in years β€” introduced in Angular 16, stable in 17. They replace many RxJS patterns for simple reactive state, enable fine-grained reactivity, and pave the way for zoneless Angular. This is THE hot interview topic in 2025.

12_signals.ts β€” Angular 16+ Signals
typescript
import { signal, computed, effect, toSignal, toObservable } from '@angular/core';

@Component({ ... })
export class CounterComponent {
  // ─── signal() β€” reactive value ────────────────────────────
  count = signal(0);                   // create signal with initial value

  count();                               // READ: call like a function
  this.count.set(5);                    // SET: replace value
  this.count.update(v => v + 1);       // UPDATE: based on previous value
  this.count.mutate(arr => arr.push(1)); // MUTATE: for objects/arrays

  // ─── computed() β€” derived signal (memoized) ───────────────
  doubled = computed(() => this.count() * 2);   // auto-recalculates
  isEven  = computed(() => this.count() % 2 === 0);

  // ─── effect() β€” side effects when signals change ──────────
  constructor() {
    effect(() => {
      console.log(`Count changed to ${this.count()}`);
      // auto-tracks all signals read inside this function
    });
  }

  // ─── Signal inputs (Angular 17.1+) β€” replaces @Input ──────
  name = input<string>('');           // signal-based @Input
  required = input.required<string>(); // required signal input
  // Usage: <app-user [name]="'Rahul'" />

  // ─── RxJS interop ─────────────────────────────────────────
  user$ = this.userService.getUser(1);         // Observable
  user = toSignal(user$, { initialValue: null }); // Observable β†’ Signal

  countSignal = signal(0);
  count$ = toObservable(countSignal);              // Signal β†’ Observable
}

// ─── In template β€” direct function call, NO async pipe needed
// <p>{{ count() }}</p>   (NOT {{ count | async }})
// <p>{{ doubled() }}</p>
Signals vs RxJS β€” Key Difference
Signals: synchronous, simple, no subscription management, perfect for UI state. RxJS: asynchronous, powerful operators, streams of data over time, HTTP, events. Use both together β€” Signals for local component state, RxJS for async/complex flows. toSignal() and toObservable() bridge them.
Complementary
Zoneless Angular
Zone.js patches async APIs to trigger change detection. Zoneless: Signals tell Angular EXACTLY which components to update, no zone.js needed. 60% better startup time. Stable in Angular 18. Set up with provideExperimentalZonelessChangeDetection().
Future of Angular
Signal Component
When all inputs are signal inputs and component only uses signals/computed, Angular can use fine-grained change detection β€” updates ONLY that component's view, not the whole subtree. Biggest performance win in Angular's history.
Angular 17+
Module 13 Β· Advanced
β—ˆ Senior

Change Detection

Change detection is how Angular knows when to update the DOM. It's the most important performance concept in Angular and heavily asked in senior interviews. OnPush is the key optimization.

Default Strategy
Angular checks EVERY component in the tree on ANY async event (click, HTTP, setTimeout, etc.) β€” via Zone.js. Simple but inefficient for large apps. Every component runs change detection even if its data didn't change.
Expensive
OnPush Strategy
changeDetection: ChangeDetectionStrategy.OnPush. Angular skips this component unless: (1) @Input reference changes, (2) event originates from this component, (3) async pipe pushes new value, (4) markForCheck() called manually. Dramatically reduces checks.
Use always
Zone.js
A library that patches browser APIs (setTimeout, fetch, Promises, DOM events) to notify Angular when async work finishes. This is how Angular knows to run change detection. Can be disabled with Signals (zoneless). Zone.js adds ~50KB to bundles.
How it works
markForCheck() vs detectChanges()
markForCheck(): mark this component and ancestors for checking in NEXT change detection cycle. detectChanges(): trigger change detection NOW synchronously. Use markForCheck() with OnPush when pushing values from non-Angular async (WebSocket, native events). detectChanges() for immediate updates.
Manual control
13_change_detection.ts
typescript
@Component({
  selector: 'app-user-list',
  changeDetection: ChangeDetectionStrategy.OnPush, // ← key optimization
  template: `
    @for (user of users(); track user.id) {
      <app-user-item [user]="user" />
    }
  `,
})
export class UserListComponent {
  users = signal<User[]>([]); // signals work perfectly with OnPush

  // ─── Immutable updates for OnPush to detect change ────────
  // ❌ this.users.push(newUser)     β€” same reference, OnPush won't detect
  // βœ… this.users.update(u => [...u, newUser]) β€” new array reference

  private cdr = inject(ChangeDetectorRef);
  updateFromWebSocket(data: User[]) {
    this.users.set(data);      // signals handle this automatically
    // Without signals: this.cdr.markForCheck();
  }
}
Module 14 Β· Advanced
β—ˆ Senior

Lazy Loading, @defer & Optimization

14_lazy_defer.ts
typescript
// ─── Route-based lazy loading ─────────────────────────────
{ path: 'reports', loadComponent: () =>
  import('./reports.component').then(m => m.ReportsComponent) }

// ─── @defer β€” Angular 17+ declarative lazy loading ────────
// In template:
@defer (on viewport) {         <!-- load when enters viewport -->
  <app-heavy-chart />
} @loading {
  <app-skeleton />
} @error {
  <p>Failed to load chart</p>
} @placeholder {             <!-- shown before defer triggers -->
  <div>Chart will appear here</div>
}

// Defer triggers:
// on idle       β€” when browser is idle
// on viewport   β€” when element enters viewport
// on interaction β€” on click/hover
// on timer(5000) β€” after 5 seconds
// when condition β€” when expression is truthy

// ─── trackBy equivalent in @for ───────────────────────────
@for (item of items; track item.id) {  // track by unique property
  <app-item [data]="item" />
}

// ─── Pure Pipe β€” cached transformation ────────────────────
@Pipe({ name: 'filter', pure: true })  // only runs when INPUT changes
export class FilterPipe implements PipeTransform {
  transform(items: any[], search: string) {
    return items.filter(i => i.name.includes(search));
  }
}
Pure vs Impure Pipes
Pure (default): pipe recalculates ONLY when input primitive value changes or object reference changes β€” very performant. Impure (pure: false): runs on every change detection cycle β€” expensive but needed for arrays/objects mutated in place. Prefer immutable updates + pure pipes.
Performance critical
async pipe
Subscribes to Observable/Promise in template, auto-unsubscribes when component destroys. With OnPush, async pipe calls markForCheck() when new value arrives. Best practice: one async pipe per Observable, use combineLatest for multiple.
Auto cleanup
Virtual Scrolling (CDK)
@angular/cdk/scrolling CdkVirtualScrollViewport renders only visible list items. Essential for lists with 1000+ items. Same concept as react-window. FixedSizeVirtualScrollStrategy for fixed height, VariableSizeVirtualScrollStrategy for dynamic.
Large lists
Module 15 Β· Advanced
β—ˆ Senior

SSR, Built-in Pipes & Testing

15_misc.ts
typescript
// ─── Built-in Pipes (know all of these) ───────────────────
{{ 1234.56 | currency:'INR':'symbol':'1.2-2' }}  // β‚Ή1,234.56
{{ today | date:'dd/MM/yyyy' }}                   // 25/02/2025
{{ text | uppercase }}{{ text | lowercase }}
{{ 0.75 | percent }}                               // 75%
{{ obj | json }}                                   // debug
{{ obs$ | async }}                                 // subscribe + unsubscribe
{{ items | slice:0:5 }}                            // first 5 items
{{ key | keyvalue }}                               // iterate object as {key,value}

// ─── Angular SSR (Angular Universal) ─────────────────────
// ng add @angular/ssr β†’ adds server-side rendering
// Renders Angular on Node.js server, sends HTML to browser
// Benefits: SEO, faster LCP, better social sharing

// isPlatformBrowser check β€” run browser code only on client:
const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
if (isBrowser) { localStorage.getItem('token'); }  // safe!

// ─── Angular Testing ──────────────────────────────────────
describe('UserComponent', () => {
  let component: UserComponent;
  let fixture: ComponentFixture<UserComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [UserComponent, HttpClientTestingModule],
      providers: [{ provide: UserService, useValue: mockUserService }],
    }).compileComponents();

    fixture = TestBed.createComponent(UserComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();               // trigger ngOnInit
  });

  it('should display user name', () => {
    component.user = { name: 'Rahul', id: '1' };
    fixture.detectChanges();
    const el = fixture.nativeElement.querySelector('h2');
    expect(el.textContent).toContain('Rahul');
  });
});
Module 16 Β· Interview Mastersheet

Complete Interview Question Bank

Beginner / Fresher β€” TCS, Infosys, Wipro
What is Angular? AngularJS vs Angular?
Angular 2+ is TypeScript-based complete rewrite. AngularJS uses controllers+$scope. Angular uses components+TypeScript. Completely different.
Easy
What are decorators in Angular?
TypeScript annotations (@Component, @Injectable, @NgModule, @Pipe, @Directive) that add metadata to classes. Angular reads this metadata to build the application.
Easy
@Input vs @Output?
@Input: parent passes data DOWN to child. @Output (EventEmitter): child sends events UP to parent. Parent β†’ Child = @Input. Child β†’ Parent = @Output.
Easy
What is two-way data binding?
[(ngModel)] β€” "banana in a box" syntax. Combines [property] binding and (event) binding. Input field changes update the model; model changes update the input field.
Easy
Mid Level / 2-4 years β€” Product companies
Explain change detection with OnPush
Default checks all components. OnPush skips component unless Input reference changes, component event, async pipe, or markForCheck(). Must use immutable updates.
Mid
switchMap vs mergeMap vs concatMap?
switchMap cancels old, mergeMap runs parallel, concatMap queues sequential. Wrong choice = wrong UX or bugs.
Mid β€” Very common
What is HttpInterceptor?
Middleware for HTTP requests/responses. Use for: adding auth headers, logging, error handling, showing loading spinner. Functional interceptors are the modern approach.
Mid
How to prevent memory leaks?
Unsubscribe in ngOnDestroy. Use takeUntil(destroy$), async pipe, or DestroyRef. BehaviorSubject needs .complete(). Forget this = app slows over time.
Mid β€” Critical
Senior / 4+ years β€” FAANG, Deutsche Bank, Goldman Sachs
Explain Angular Signals and their benefits
Fine-grained reactivity without Zone.js. signal(), computed(), effect(). Enables zoneless architecture. Simpler than RxJS for local state. Future of Angular.
Senior β€” Hot 2025
NgRx architecture β€” all 5 pieces?
Store (state), Actions (events), Reducers (state transitions), Effects (side effects/API calls), Selectors (memoized computed state). Unidirectional data flow.
Senior
DI hierarchy and injection tokens?
Root→Module→Component injectors. Component providers create isolated instances. InjectionToken for non-class values. useClass, useValue, useFactory, useExisting.
Senior
NgModule vs Standalone β€” migrate strategy?
Use ng generate --standalone. Convert leaf components first. Use schematic: ng generate @angular/core:standalone. Keep ngOnInit behavior, remove module declarations.
Senior 2025
🎯 Angular Interview Readiness

You're ready when you can explain all of these:

  • Difference between @Component decorator options and what each property does
  • Draw the Angular change detection tree and explain Default vs OnPush
  • Write a complete NgRx feature (actions + reducer + effects + selectors)
  • Explain switchMap vs mergeMap vs concatMap with real use cases
  • Create a functional HTTP interceptor that adds auth token
  • Implement a reactive form with custom validators and FormArray
  • Explain Signals β€” signal(), computed(), effect() β€” and when to use vs RxJS
  • Set up lazy loading and @defer blocks for performance
  • Fix a memory leak in an Angular component with multiple subscriptions