Skip to content

Angular Interview Questions & Answers

A comprehensive Q&A reference for Angular developers — from beginner to advanced. Includes theory, code examples, and real-world use cases.

ng new my-angular-app -> Create a New Angular App


🅰 Angular 17 Topics

🔹 New Features (Angular 17)

  • Signals (reactive state management)
  • Improved control flow (@if, @for)
  • deferrable views (@defer)
  • Standalone components
  • Better performance

***

which design pattern you use in your Angular project

"In my Angular projects, I primarily use the Component-based architecture that Angular provides. For business logic and API communication, I follow the Service pattern with Dependency Injection. For state management, I use Angular Signals (or NgRx if applicable), which follows reactive and Observer patterns. I also use the Facade pattern to keep components independent of the state management implementation. For data access, I separate API calls into services or repository-like layers. Additionally, I use Route Guards for authorization and Smart/Presentational component separation to improve maintainability."

Table of Contents

  1. Angular Core Fundamentals
  2. 1.1 Default Libraries in Angular
  3. 1.2 zone.js and webpack
  4. 1.3 angular.json
  5. 1.4 Components
  6. 1.5 Component Decorator
  7. 1.6 Rendering a Component
  8. 1.7 Standalone Components
  9. 1.8 Single File Components
  10. Lifecycle Events
  11. 2.1 What are Lifecycle Events?
  12. 2.2 Types of Lifecycle Events
  13. 2.3 Most Used Events and Use Cases
  14. 2.4 Constructor vs Lifecycle Events
  15. Data Binding
  16. 3.1 One-Way Data Binding
  17. 3.2 Two-Way Data Binding
  18. Directives
  19. 4.1 Structural Directives
  20. 4.2 Attribute Directives
  21. Control Flow Statements
  22. Signals
  23. 6.1 What is a Signal?
  24. 6.2 Creating a Signal
  25. 6.3 Accessing a Signal
  26. 6.4 Updating a Signal
  27. 6.5 Signals and Change Detection
  28. Linked Signal
  29. Routing
  30. 8.1 What is Routing?
  31. 8.2 Creating Routes
  32. 8.3 router-outlet
  33. 8.4 Programmatic Navigation
  34. 8.5 routerLink
  35. Pipes
  36. Resource API
  37. Component Communication
  38. Angular CLI — Global vs Local Install
  39. RxJS
  40. Route Guards
  41. JWT Security Best Practices

1. Angular Core Fundamentals

1.1 What are the default libraries installed with Angular and what is their unique purpose?

When you run ng new, Angular installs the following core libraries:

Library Purpose
@angular/core Core decorators, DI system, component engine
@angular/common Built-in directives (NgIf, NgFor), pipes (DatePipe, etc.)
@angular/forms Template-driven and reactive forms
@angular/router Client-side routing and navigation
@angular/platform-browser DOM rendering in browser environment
@angular/platform-browser-dynamic JIT compilation support
@angular/animations Animation support for components
@angular/compiler Compiles templates at build time (AOT)
rxjs Reactive programming — powers HttpClient, Router, Forms
zone.js Tracks async tasks to trigger change detection
tslib TypeScript runtime helpers

1.2 What is the role of zone.js and webpack in Angular?

zone.js

Theory: zone.js is a library that patches all async APIs in JavaScript (setTimeout, Promise, XHR, event listeners, etc.) so Angular knows when async operations complete and when to run change detection.

User clicks button
    → event fires inside Zone
    → zone.js patches the event
    → Angular gets notified
    → Change detection runs
    → DOM updates

Real Use Case: Without zone.js, if you do setTimeout(() => this.name = 'John', 1000), Angular wouldn't know to re-render. zone.js intercepts the timer and triggers a check.

🔑 Signals (Angular 17+) are designed to eventually remove the need for zone.js — components can use ChangeDetectionStrategy.OnPush and signals to be fully zone-free.

webpack

Theory: webpack is the module bundler Angular uses internally (via @angular-devkit). It:

  • Bundles all TypeScript, CSS, and HTML files into optimized JS bundles
  • Tree-shakes unused code
  • Handles code splitting for lazy-loaded routes
  • Generates main.js, polyfills.js, styles.css in the dist/ folder

In Angular 17+, esbuild is the default builder (faster), but webpack is still supported.


1.3 What is angular.json and what are its key options?

Theory: angular.json is the workspace configuration file for the Angular CLI. It defines how the project is built, served, and tested.

Key sections:

{
  "projects": {
    "my-app": {
      "architect": {
        "build": {
          "options": {
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": ["zone.js"],
            "tsConfig": "tsconfig.app.json",
            "assets": ["src/assets"],
            "styles": ["src/styles.css"],
            "scripts": [],
            "outputPath": "dist/my-app"
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.production.ts"
                }
              ],
              "optimization": true
            }
          }
        }
      }
    }
  }
}

index.html — The shell HTML page. Angular injects <app-root> here and the CLI injects script tags automatically.

main.ts — The JavaScript entry point that bootstraps the Angular application:

import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';

bootstrapApplication(AppComponent, appConfig);

1.4 What is a Component and how do you create one?

Theory: A component is the fundamental building block of an Angular application. Every component has:

  • A TypeScript class (logic)
  • An HTML template (view)
  • CSS styles (appearance)

Creating via CLI (recommended):

ng generate component pages/user-profile
# shorthand:
ng g c pages/user-profile

Creating manually:

import { Component } from '@angular/core';

@Component({
  selector: 'app-user-profile',
  standalone: true,
  templateUrl: './user-profile.component.html',
  styleUrl: './user-profile.component.css'
})
export class UserProfileComponent {
  username = 'John Doe';
}

Real Use Case: Break the app into reusable pieces — HeaderComponent, FooterComponent, UserCardComponent — each responsible for one thing.


1.5 What is the Component Decorator?

🔹 Decorators

  • Decorators are special TypeScript functions that provide metadata to Angular classes, properties, methods, or parameters. They tell Angular how a class or member should be processed.

  • @Component -> Defines a component.

  • @Injectable -> Marks a class as available for dependency injection.
  • @Directive
  • @Input – Receives data from a parent component.
  • @Output – Sends data/events to a parent component. @Pipe – Defines a pipe.

Theory: The @Component decorator is a metadata function that tells Angular how to process and render a class as a component.

@Component({
  selector: 'app-hero',
  standalone: true,
  imports: [CommonModule],
  template: `<h1>{{ title }}</h1>`,
  styles: [`h1 { color: red; }`],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class HeroComponent {
  title = 'Superman';
}
Property Purpose
selector How the component is used in HTML
standalone If true, no NgModule required
templateUrl Path to external HTML file
styleUrl Path to external CSS file
imports Dependencies needed by the template
changeDetection When Angular checks for changes

1.6 How do you render a Component?

Method 1 — In a template:

<!-- parent.component.html -->
<app-hero></app-hero>
<app-user-profile [userId]="123"></app-user-profile>

Don't forget to add it to the parent's imports array:

@Component({
  imports: [HeroComponent, UserProfileComponent],
  ...
})
export class ParentComponent {}

Method 2 — Via routing:

{ path: 'hero', component: HeroComponent }
<!-- layout.html -->
<router-outlet></router-outlet>

1.7 What is a Standalone Component?

Theory: A standalone component does not belong to any NgModule. It manages its own dependencies via the imports array in @Component. This is the default in Angular 17+.

Before (Module-based):

@NgModule({
  declarations: [HeroComponent],
  imports: [CommonModule],
})
export class HeroModule {}

After (Standalone — Angular 17+):

@Component({
  selector: 'app-hero',
  standalone: true,
  imports: [CommonModule],
  template: `...`
})
export class HeroComponent {}

Why standalone?

  • Simpler — no need to maintain NgModules
  • Better tree-shaking (only imports what's actually used)
  • Easier lazy loading per component

1.8 How do you create a single file component and when should you use it?

Theory: A single file component has the template and styles inline inside the .ts file.

@Component({
  selector: 'app-badge',
  standalone: true,
  template: `
    <span class="badge" [class]="color">{{ label }}</span>
  `,
  styles: [`
    .badge { padding: 4px 8px; border-radius: 4px; color: white; }
    .primary { background: blue; }
    .danger  { background: red; }
  `]
})
export class BadgeComponent {
  label = 'New';
  color = 'primary';
}

When to use:

Use single file Use separate files
Small, simple UI pieces Complex templates (>20 lines)
Reusable widgets (badge, spinner, avatar) Components with heavy styling
Prototyping / demos Production feature components

2. Lifecycle Events

2.1 What are Lifecycle Events?

Theory: Lifecycle hooks are methods Angular calls automatically at key moments during a component's life — from creation to destruction.

Constructor → ngOnChanges → ngOnInit → ngDoCheck
    → ngAfterContentInit → ngAfterContentChecked
    → ngAfterViewInit → ngAfterViewChecked
    → (repeat on changes)
    → ngOnDestroy
export class MyComponent implements OnInit, OnDestroy {
  ngOnInit() { /* runs once after component is created */ }
  ngOnDestroy() { /* runs before component is destroyed */ }
}

2.2 What are the types of Lifecycle Events?

Hook When it runs Common Use
ngOnChanges When @Input values change React to parent data changes
ngOnInit Once after first ngOnChanges API calls, form init
ngDoCheck Every change detection cycle Custom change detection
ngAfterContentInit After <ng-content> is projected Access projected content
ngAfterContentChecked After projected content is checked Respond to content changes
ngAfterViewInit After component's view is rendered DOM access, @ViewChild
ngAfterViewChecked After view and children are checked Performance-sensitive updates
ngOnDestroy Just before component is removed Cleanup subscriptions, timers

ngOnInit — API call example:

ngOnInit(): void {
  this.userService.getUser(this.userId).subscribe(user => {
    this.user = user;
  });
}

ngAfterViewInit — DOM access:

@ViewChild('myCanvas') canvasRef!: ElementRef;

ngAfterViewInit(): void {
  // Safe to access DOM here — view is fully rendered
  const ctx = this.canvasRef.nativeElement.getContext('2d');
  ctx.fillRect(10, 10, 100, 100);
}

ngOnDestroy — Cleanup:

private subscription!: Subscription;

ngOnInit() {
  this.subscription = this.dataService.data$.subscribe(d => this.data = d);
}

ngOnDestroy() {
  this.subscription.unsubscribe();  // Prevent memory leaks
  clearInterval(this.timer);
}

2.3 What are the most used Lifecycle Events and their use cases?

Hook Most Common Use Case
ngOnInit Fetch initial data from API, initialize reactive forms
ngOnChanges Recompute derived values when @Input changes
ngAfterViewInit Access @ViewChild, initialize third-party DOM libraries
ngOnDestroy Unsubscribe Observables, clear timers, detach event listeners

Real Use Case — Auto-refreshing dashboard:

export class DashboardComponent implements OnInit, OnDestroy {
  stats: Stats[] = [];
  private autoRefresh!: Subscription;

  ngOnInit(): void {
    this.loadStats();
    this.autoRefresh = interval(30000).subscribe(() => this.loadStats());
  }

  private loadStats(): void {
    this.statsService.get().subscribe(s => this.stats = s);
  }

  ngOnDestroy(): void {
    this.autoRefresh.unsubscribe(); // Stop polling when user leaves page
  }
}

2.4 What is the difference between Constructor and Lifecycle Events?

constructor ngOnInit
When Class is instantiated by DI After Angular sets @Input bindings
Purpose Inject dependencies Business logic, API calls, form setup
@Input available? No Yes
DOM available? No No (use ngAfterViewInit)
export class UserComponent implements OnInit {
  @Input() userId!: number;

  // Only inject services here
  constructor(private userService: UserService) {}

  // Use @Input() values and run business logic here
  ngOnInit(): void {
    this.userService.getById(this.userId).subscribe(u => this.user = u);
  }
}

Never make API calls in the constructor — @Input values haven't been set yet.


3. Data Binding

3.1 What is One-Way Data Binding?

Theory: Data flows in one direction — from component class to template, or template to component via events.

Interpolation {{ }}

Displays a component property as text:

export class AppComponent {
  username = 'Alice';
  today = new Date();
}
<p>Hello, {{ username }}!</p>
<p>Today: {{ today | date }}</p>

Interpolation always produces a string. For non-string DOM properties use property binding.

Property Binding [ ]

Binds a component property to a DOM property or component @Input:

<input [value]="username" [placeholder]="'Enter ' + username">
<button [disabled]="isLoading">Save</button>
<div [class.active]="isActive"></div>
<img [src]="imageUrl" [alt]="imageAlt">

<!-- Component @Input binding -->
<app-user-card [user]="currentUser" [showAvatar]="true"></app-user-card>

Event Binding ( )

Listens to DOM events and calls component methods:

<button (click)="saveUser()">Save</button>
<input (keyup)="onSearch($event)" (blur)="validate()">
<form (ngSubmit)="onSubmit()">...</form>
onSearch(event: Event): void {
  const term = (event.target as HTMLInputElement).value;
  this.filter(term);
}

3.2 What is Two-Way Data Binding?

Theory: Data flows both ways — component and template stay in sync automatically.

Method 1 — Template-Driven Forms [(ngModel)]

Requires FormsModule:

@Component({
  standalone: true,
  imports: [FormsModule],
  template: `
    <input [(ngModel)]="firstName" placeholder="First name">
    <p>Hello, {{ firstName }}!</p>
  `
})
export class FormComponent {
  firstName = '';
}

[(ngModel)] is called "banana in a box" — [] binds the property, () emits changes.

Method 2 — Reactive Forms formControlName

Requires ReactiveFormsModule:

@Component({
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form" (ngSubmit)="submit()">
      <input formControlName="firstName">
      <small *ngIf="form.get('firstName')?.invalid">Required</small>
      <button type="submit">Submit</button>
    </form>
  `
})
export class ReactiveFormComponent implements OnInit {
  form!: FormGroup;

  ngOnInit() {
    this.form = new FormGroup({
      firstName: new FormControl('', [Validators.required, Validators.minLength(2)])
    });
  }

  submit() {
    console.log(this.form.value);
  }
}
[(ngModel)] formControlName
Module FormsModule ReactiveFormsModule
Type Template-driven Code-driven
Validation In HTML In TypeScript
Best for Simple forms Complex, dynamic forms

Method 3 — Custom [()] on Components

// counter.component.ts
@Component({
  selector: 'app-counter',
  template: `<button (click)="increment()">{{ value }}</button>`
})
export class CounterComponent {
  @Input() value = 0;
  @Output() valueChange = new EventEmitter<number>(); // must end with "Change"

  increment() {
    this.value++;
    this.valueChange.emit(this.value);
  }
}
<!-- parent.html -->
<app-counter [(value)]="count"></app-counter>
<p>Count: {{ count }}</p>

4. Directives

Directives are classes that add behavior or modify the appearance of DOM elements. * Component Directives -> Every Angular component is a directive. * Structural (*ngIf, *ngFor) → Change the DOM layout by adding/removing elements. * Attribute (ngClass, ngStyle)→ Change the appearance or behavior of an existing element.

4.1 What are Structural Directives?

Theory: Structural directives change the DOM structure — they add, remove, or repeat elements. Prefixed with *.

@if (Angular 17+):

@if (isLoggedIn) {
  <app-dashboard />
} @else {
  <app-login />
}

@for (Angular 17+):

@for (user of users; track user.id) {
  <app-user-card [user]="user" />
} @empty {
  <p>No users found.</p>
}

@switch:

@switch (role) {
  @case ('admin') { <app-admin-panel /> }
  @case ('user')  { <app-dashboard /> }
  @default        { <app-login /> }
}

Custom structural directive:

@Directive({ selector: '[appHasRole]', standalone: true })
export class HasRoleDirective {
  constructor(
    private template: TemplateRef<any>,
    private viewContainer: ViewContainerRef,
    private auth: AuthService
  ) {}

  @Input() set appHasRole(role: string) {
    if (this.auth.hasRole(role)) {
      this.viewContainer.createEmbeddedView(this.template);
    } else {
      this.viewContainer.clear();
    }
  }
}
<button *appHasRole="'admin'">Delete User</button>

4.2 What are Attribute Directives?

Theory: Attribute directives change the appearance or behavior of an existing element without modifying the DOM structure.

Built-in:

<div [ngClass]="{ 'active': isActive, 'disabled': !isEnabled }">...</div>
<p [ngStyle]="{ 'color': textColor, 'font-size': fontSize + 'px' }">...</p>

Custom attribute directive:

@Directive({
  selector: '[appHighlight]',
  standalone: true
})
export class HighlightDirective {
  @Input() appHighlight = 'yellow';

  constructor(private el: ElementRef, private renderer: Renderer2) {}

  @HostListener('mouseenter')
  onMouseEnter() {
    this.renderer.setStyle(this.el.nativeElement, 'background', this.appHighlight);
  }

  @HostListener('mouseleave')
  onMouseLeave() {
    this.renderer.removeStyle(this.el.nativeElement, 'background');
  }
}
<p appHighlight="lightblue">Hover over me!</p>

Real Use Case: Permission-based visibility, auto-focus, tooltip on hover, input masking.


5. Control Flow Statements

Theory: Angular 17+ introduced built-in control flow syntax as a faster, import-free replacement for *ngIf, *ngFor, *ngSwitch.

<!-- Conditional rendering -->
@if (user.isAdmin) {
  <span class="badge bg-danger">Admin</span>
} @else if (user.isPremium) {
  <span class="badge bg-warning">Premium</span>
} @else {
  <span class="badge bg-secondary">User</span>
}

<!-- List rendering with empty state -->
@for (product of products; track product.id) {
  <app-product-card [product]="product" />
} @empty {
  <p class="text-muted">No products available.</p>
}

<!-- Deferred / lazy rendering -->
@defer (on viewport) {
  <app-heavy-chart [data]="chartData" />
} @placeholder {
  <div class="skeleton-chart"></div>
} @loading (minimum 500ms) {
  <app-spinner />
}

track in @for is required. Use a unique identifier like item.id for optimal DOM reuse.


6. Signals

6.1 What is a Signal?

Theory: A Signal is a reactive primitive (Angular 16+) that holds a value and automatically notifies Angular when it changes — without relying on zone.js.

Signal value changes → Angular is notified → Only affected UI parts re-render

vs. Regular variables:

// Regular variable — zone.js must catch the change
name = 'Alice';
changeName() { this.name = 'Bob'; }

// Signal — Angular always knows when it changes
name = signal('Alice');
changeName() { this.name.set('Bob'); }

6.2 How do you create a Signal?

import { signal, computed, effect } from '@angular/core';

export class CounterComponent {
  // Writable signal
  count = signal(0);

  // Signal with explicit type
  user = signal<User | null>(null);

  // Computed signal (derived, read-only)
  doubleCount = computed(() => this.count() * 2);
  isLoggedIn  = computed(() => this.user() !== null);
}

6.3 How do you access a Signal?

Call the signal as a function:

// In TypeScript
console.log(this.count());      // 0
console.log(this.user()?.name); // 'Alice'
<!-- In template -->
<p>Count: {{ count() }}</p>
<p>Double: {{ doubleCount() }}</p>

@if (user()) {
  <p>Welcome, {{ user()!.name }}</p>
}

6.4 How do you update a Signal using set and update?

count = signal(0);

// set() — replaces the value entirely
this.count.set(10);

// update() — transforms based on current value
this.count.update(current => current + 1);
this.count.update(n => n * 2);

// Object signals — immutable update
user = signal<User>({ name: 'Alice', age: 25 });
this.user.update(u => ({ ...u, age: 26 }));
Method When to use
set(value) You have a completely new value
update(fn) New value depends on the current value

6.5 How do Signals work with Change Detection?

Theory: Signals enable fine-grained change detection — Angular only re-renders components that actually read a changed signal.

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `<p>{{ count() }}</p>`
})
export class SignalComponent {
  count = signal(0);

  constructor() {
    // effect() runs side effects whenever tracked signals change
    effect(() => {
      console.log('Count changed:', this.count());
      localStorage.setItem('count', String(this.count()));
    });
  }
}

Zoneless Angular (future):

bootstrapApplication(AppComponent, {
  providers: [provideZonelessChangeDetection()]
});

7. Linked Signal

What is a Linked Signal and how do you create one?

Theory: A linked signal (Angular 17.1+) is a writable signal whose value automatically recalculates when its dependencies change, but can also be manually overridden. It is a hybrid of signal() and computed().

import { signal, linkedSignal } from '@angular/core';

selectedCategory = signal<string>('electronics');

// Resets to default item whenever selectedCategory changes
selectedItem = linkedSignal(() => {
  const category = this.selectedCategory();
  return this.getDefaultItem(category);
});

changeCategory(cat: string) {
  this.selectedCategory.set(cat); // selectedItem auto-resets
}

overrideItem(item: Item) {
  this.selectedItem.set(item); // manual override still works
}

Real Use Case — Pagination reset on filter change:

filter      = signal('');
currentPage = linkedSignal(() => {
  this.filter(); // track filter dependency
  return 1;      // reset to page 1 whenever filter changes
});
* Signal: A reactive variable that automatically updates the UI when its value changes. - Writable Signal – Read and update value. const count = signal(0); - Computed Signal – Derived read-only value. const doubleCount = computed(() => count() * 2); - Effect – Runs code when a signal changes. effect(() => console.log(count())); - Linked Signal is a writable signal that derives its value from another signal while allowing direct updates, combining the benefits of writable and computed signals. - computed() is used for read-only derived state and cannot be modified manually, whereas linkedSignal() creates a writable signal that stays in sync with a source signal but also allows local updates.


8. Routing

8.1 What is Routing?

Theory: Routing lets Angular apps navigate between views without full page reloads. The Router maps URL paths to components and renders them inside <router-outlet>.

URL changes → Router matches path → Component renders in <router-outlet>

8.2 How do you create Routes?

// app.routes.ts
export const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },

  // Eager-loaded
  { path: 'home', component: HomeComponent },

  // Lazy-loaded (better performance)
  {
    path: 'dashboard',
    loadComponent: () =>
      import('./pages/dashboard/dashboard.component').then(m => m.DashboardComponent)
  },

  // Protected with guard
  {
    path: 'admin',
    loadComponent: () =>
      import('./pages/admin/admin.component').then(m => m.AdminComponent),
    canActivate: [authGuard]
  },

  // Route parameter
  { path: 'users/:id', component: UserDetailComponent },

  // Wildcard 404
  { path: '**', redirectTo: '/home' }
];

Register in app.config.ts:

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes)]
};

8.3 What is router-outlet?

Theory: <router-outlet> is a placeholder directive where the Router renders the matched component.

<!-- layout.component.html -->
<nav>...</nav>
<main>
  <router-outlet></router-outlet>
</main>
<footer>...</footer>

Named outlets for side panels:

<router-outlet></router-outlet>
<router-outlet name="sidebar"></router-outlet>

8.4 How do you navigate programmatically?

export class LoginComponent {
  private router = inject(Router);
  private activatedRoute = inject(ActivatedRoute);

  onLoginSuccess(): void {
    // Absolute path string
    this.router.navigateByUrl('/dashboard');

    // Array of path segments
    this.router.navigate(['/users', userId]);

    // With query params
    this.router.navigate(['/cases'], { queryParams: { status: 'open' } });

    // Relative navigation
    this.router.navigate(['../profile'], { relativeTo: this.activatedRoute });
  }
}

Theory: routerLink is a directive that turns any element into a navigation link without a full page reload.

<a routerLink="/home">Home</a>

<!-- Dynamic path -->
<a [routerLink]="['/users', user.id]">View Profile</a>

<!-- Query params -->
<a [routerLink]="['/cases']" [queryParams]="{ status: 'open' }">Open Cases</a>

<!-- Active class -->
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>

<!-- Exact match -->
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">Home</a>

Import RouterLink and RouterLinkActive in standalone components' imports array.


9. Pipes

What are Pipes and how do you create a custom one?

Theory: Pipes transform data in templates using the | operator without modifying the underlying data.

Built-in Pipes:

<p>{{ today | date:'mediumDate' }}</p>        <!-- Apr 29, 2026 -->
<p>{{ price | currency:'USD' }}</p>           <!-- $1,234.56 -->
<p>{{ 0.75 | percent }}</p>                   <!-- 75% -->
<p>{{ name | uppercase }}</p>                 <!-- ALICE -->
<p>{{ longText | slice:0:100 }}...</p>        <!-- first 100 chars -->
<p>{{ user | json }}</p>                      <!-- { "name": "Alice" } -->
<p>{{ items$ | async }}</p>                   <!-- unwraps Observable -->

Creating a Custom Pipe:

// pipes/truncate.pipe.ts
@Pipe({ name: 'truncate', standalone: true })
export class TruncatePipe implements PipeTransform {
  transform(value: string, maxLength = 100, suffix = '...'): string {
    if (!value) return '';
    return value.length > maxLength
      ? value.substring(0, maxLength) + suffix
      : value;
  }
}
<p>{{ description | truncate:50 }}</p>
<p>{{ longTitle | truncate:20:'...' }}</p>

Real Use Case — Status label pipe:

@Pipe({ name: 'statusLabel', standalone: true })
export class StatusLabelPipe implements PipeTransform {
  transform(status: string): string {
    const labels: Record<string, string> = {
      'in-progress': 'In Progress',
      'review': 'Under Review',
      'completed': 'Done',
      'draft': 'Draft'
    };
    return labels[status] ?? status;
  }
}
<span class="badge">{{ caseStatus | statusLabel }}</span>

10. Resource API

What is the Resource API, and what are refresh/reload and loader?

Theory: The Resource API (Angular 19+) is a built-in, signal-based way to handle async data fetching — replacing the common pattern of manual subscribe + loading flag + error handling.

import { resource, signal } from '@angular/core';
import { firstValueFrom } from 'rxjs';

export class UserListComponent {
  private http = inject(HttpClient);

  searchTerm = signal('');

  usersResource = resource({
    request: () => ({ search: this.searchTerm() }),
    loader: ({ request }) =>
      firstValueFrom(
        this.http.get<User[]>(`/api/users?q=${request.search}`)
      )
  });
}
@if (usersResource.isLoading()) {
  <app-spinner />
} @else if (usersResource.error()) {
  <p class="error">{{ usersResource.error() }}</p>
} @else {
  @for (user of usersResource.value(); track user.id) {
    <app-user-card [user]="user" />
  }
}

<button (click)="usersResource.reload()">Refresh</button>
Signal / Method Purpose
.value() The loaded data (undefined while loading)
.isLoading() true while request is in flight
.error() Error thrown by loader
.reload() Re-runs loader with same request
.set(value) Manually override the value

Real Use Case: Auto-refresh a case list when a filter signal changes; skeleton loaders; retry-on-error buttons.


11. Component Communication

What is @Input / @Output and how do you create a reusable component?

Theory:

  • @Input — passes data parent to child
  • @Output — sends events child to parent

Parent to Child — @Input and @Output:

// user-card.component.ts (child)
@Component({
  selector: 'app-user-card',
  template: `
    <div class="card">
      <h3>{{ user.name }}</h3>
      <p>{{ user.email }}</p>
      <button (click)="onEdit()">Edit</button>
    </div>
  `
})
export class UserCardComponent {
  @Input({ required: true }) user!: User;
  @Input() showActions = true;
  @Output() editClicked = new EventEmitter<User>();

  onEdit() {
    this.editClicked.emit(this.user);
  }
}
<!-- parent.component.html -->
<app-user-card
  [user]="currentUser"
  [showActions]="isAdmin"
  (editClicked)="onUserEdit($event)">
</app-user-card>
// parent.component.ts
onUserEdit(user: User): void {
  this.router.navigate(['/users', user.id, 'edit']);
}

Modern signal-based inputs (Angular 17.1+):

export class UserCardComponent {
  user = input.required<User>();
  showActions = input(true);
  editClicked = output<User>();

  onEdit() {
    this.editClicked.emit(this.user());
  }
}

Reusable component checklist:

  1. Accept all data via @Input — no direct service calls inside the component
  2. Communicate up via @Output events — don't navigate internally
  3. Keep it presentational (dumb) — no business logic
  4. Provide sensible @Input defaults
  5. Use OnPush change detection for performance

12. Angular CLI — Global vs Local Install

Is it necessary to install Angular globally? When should you choose global vs local?

Short answer: No — a global install is not required. It depends on your workflow.

When global install helps

  • You work on Angular projects frequently
  • You want to run ng new, ng serve from anywhere without npx
  • You prefer convenience over strict version control
npm install -g @angular/cli
ng new my-project   # works from any directory

When to avoid global install

  • Working on multiple projects with different Angular versions
  • In team environments where version consistency matters
  • In CI/CD pipelines where reproducibility is critical
# Install locally per project
npm install @angular/cli --save-dev

# Run using npx or npm scripts
npx ng serve
npm run start

Best Practice in Teams

Use local install + npm scripts so every developer and CI pipeline uses the same version:

{
  "scripts": {
    "start": "ng serve",
    "build": "ng build"
  },
  "devDependencies": {
    "@angular/cli": "^19.0.0"
  }
}
npm run start  # always uses the local Angular CLI version

13. RxJS

13.1 What is RxJS and why is it used in Angular?

Theory: RxJS (Reactive Extensions for JavaScript) is a library for reactive programming using Observables — a composable way to handle async operations, events, and data streams.

Angular Feature How RxJS is used
HttpClient All methods return Observable
ReactiveFormsModule control.valueChanges is an Observable
Router router.events is an Observable
ActivatedRoute route.paramMap is an Observable
NgRx (State) Built entirely on RxJS

Why RxJS — composable async:

// Without RxJS — callback hell
getUser(id, (user) => {
  getOrders(user.id, (orders) => {
    getDetails(orders[0].id, (details) => { /* deeply nested */ });
  });
});

// With RxJS — readable pipeline
this.userService.getUser(id).pipe(
  switchMap(user   => this.orderService.getOrders(user.id)),
  switchMap(orders => this.orderService.getDetails(orders[0].id))
).subscribe(details => this.details = details);

13.2 What is an Observable?

Theory: An Observable is like a subscription — you subscribe once and keep receiving data over time. It can emit multiple values, error out, or complete.

import { Observable } from 'rxjs';

// Creating an Observable
const counter$ = new Observable<number>(subscriber => {
  let count = 0;
  const id = setInterval(() => {
    subscriber.next(count++);
    if (count > 5) {
      subscriber.complete();
      clearInterval(id);
    }
  }, 1000);
});

// Subscribing
const sub = counter$.subscribe({
  next:     value => console.log('Value:', value),   // 0, 1, 2, 3, 4, 5
  error:    err   => console.error('Error:', err),
  complete: ()    => console.log('Done!')
});

// Always unsubscribe to prevent memory leaks
sub.unsubscribe();

Common RxJS operators:

this.searchControl.valueChanges.pipe(
  debounceTime(300),                    // Wait for user to stop typing
  filter(term => term.length > 2),      // Skip short queries
  switchMap(term =>                      // Cancel previous, start new request
    this.searchService.search(term)
  ),
  catchError(err => {                    // Handle errors gracefully
    this.error = err.message;
    return of([]);
  })
).subscribe(results => this.results = results);

13.3 What is the difference between a Promise and an RxJS Observable?

Feature Promise Observable
Values One value (resolved once) Multiple values over time
Lazy? No — executes immediately Yes — only runs on .subscribe()
Cancelable? No Yes — .unsubscribe()
Operators .then(), .catch() 100+ operators (map, filter, switchMap, ...)
Best for Single async result Streams, events, retries
// Promise — fires immediately
const promise = fetch('/api/user').then(r => r.json());

// Observable — lazy, cancelable
const user$ = this.http.get<User>('/api/user');
// Nothing happens until subscribe:
const sub = user$.subscribe(user => console.log(user));
sub.unsubscribe(); // can cancel mid-flight

13.4 Why does HttpClientProvider need to be configured at app level?

Theory: provideHttpClient() registers the HttpClient service and its interceptor pipeline in Angular's root injector. Because HttpClient is used across the entire app, providing it at the root level ensures:

  • All components and services share one instance
  • Interceptors run for every request across the entire app
// app.config.ts
export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withInterceptors([authInterceptor])
    )
  ]
};

If provided inside a component, each component instance gets its own HttpClient — interceptors won't be shared.


14. Route Guards

14.1 What are the Types of Route Guards?

Theory: Route guards control navigation — they decide whether a user can enter or leave a route.

Guard Type Purpose
canActivate CanActivateFn Can user access this route?
canActivateChild CanActivateChildFn Can user access child routes?
canDeactivate CanDeactivateFn Can user leave this route? (e.g., unsaved changes)
canMatch CanMatchFn Should this route definition be matched at all?
resolve ResolveFn Pre-fetch data before the route activates
// auth.guard.ts
export const authGuard: CanActivateFn = (route, state) => {
  const auth = inject(AuthService);
  const router = inject(Router);
  return auth.isLoggedIn()
    ? true
    : router.createUrlTree(['/login'], { queryParams: { returnUrl: state.url } });
};

// unsaved-changes.guard.ts
export const unsavedChangesGuard: CanDeactivateFn<EditFormComponent> = (component) => {
  if (component.hasUnsavedChanges()) {
    return confirm('You have unsaved changes. Leave anyway?');
  }
  return true;
};
// Wiring in routes
export const routes: Routes = [
  {
    path: 'profile/edit',
    component: EditProfileComponent,
    canActivate: [authGuard],
    canDeactivate: [unsavedChangesGuard]
  }
];

14.2 Why write Function-based Guards instead of Class-based Guards?

Class-based (old way):

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private auth: AuthService, private router: Router) {}

  canActivate(): boolean | UrlTree {
    return this.auth.isLoggedIn()
      ? true
      : this.router.createUrlTree(['/login']);
  }
}

Function-based (recommended — Angular 15+):

export const authGuard: CanActivateFn = () => {
  const auth = inject(AuthService);
  const router = inject(Router);
  return auth.isLoggedIn() ? true : router.createUrlTree(['/login']);
};

Why functions are preferred:

Reason Explanation
Less boilerplate No class, no @Injectable(), no constructor
inject() instead of constructor DI Works cleanly inside functions
Better tree-shaking Build tools can optimize unused code more easily
Angular direction Angular is standardizing on functional APIs

14.3 When should you use Class-based Guards instead of Functions?

1. Shared logic across multiple guards:

export abstract class BaseAuthGuard {
  protected auth = inject(AuthService);
  protected router = inject(Router);

  protected redirectToLogin(): UrlTree {
    return this.router.createUrlTree(['/login']);
  }
}

@Injectable({ providedIn: 'root' })
export class AdminGuard extends BaseAuthGuard implements CanActivate {
  canActivate(): boolean | UrlTree {
    return this.auth.isAdmin() ? true : this.redirectToLogin();
  }
}

2. Guard needs to hold state:

@Injectable({ providedIn: 'root' })
export class NavigationHistoryGuard implements CanActivate {
  private history: string[] = [];  // stateful

  canActivate(route: ActivatedRouteSnapshot): boolean {
    this.history.push(route.url.join('/'));
    return true;
  }

  getHistory(): string[] { return this.history; }
}

3. Complex multi-service dependencies:

@Injectable({ providedIn: 'root' })
export class FeatureFlagGuard implements CanActivate {
  constructor(
    private flags: FeatureFlagService,
    private config: AppConfigService,
    private analytics: AnalyticsService
  ) {}

  canActivate(route: ActivatedRouteSnapshot): boolean {
    const feature = route.data['feature'];
    this.analytics.track('route-guard-check', feature);
    return this.flags.isEnabled(feature) && this.config.isAllowed(feature);
  }
}

Decision Guide:

Need shared logic across guards?       → Class
Need to store state inside the guard?  → Class
Complex multi-service dependency?      → Class
Simple auth check?                     → Function
Single service check?                  → Function
New project (Angular 15+)?             → Function (default)

15. JWT Security Best Practices

What are the best practices for storing JWT tokens in Angular? How do you hide them from users?

Storage options compared:

Storage XSS Risk CSRF Risk Accessible to JS Recommendation
localStorage High None Yes Avoid for tokens
sessionStorage High None Yes Short sessions only
Cookie (no flags) High High Yes Never
HttpOnly Cookie None Medium No Best for refresh token
Memory (service signal) Low None Scoped to tab Best for access token

Recommended Pattern — Store access token in memory only:

// auth.service.ts
@Injectable({ providedIn: 'root' })
export class AuthService {
  // Private signal — not persisted, not accessible from outside
  private accessToken = signal<string | null>(null);
  private userRole    = signal<string | null>(null);

  setTokens(accessToken: string, role: string): void {
    this.accessToken.set(accessToken);
    this.userRole.set(role);
    // Never do: localStorage.setItem('token', accessToken)
  }

  getAccessToken(): string | null { return this.accessToken(); }

  isLoggedIn = computed(() => this.accessToken() !== null);
  role       = computed(() => this.userRole());

  logout(): void {
    this.accessToken.set(null);
    this.userRole.set(null);
  }
}
// auth.interceptor.ts — attaches token from memory to every request
export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const auth  = inject(AuthService);
  const token = auth.getAccessToken();

  if (token) {
    req = req.clone({ setHeaders: { Authorization: `Bearer ${token}` } });
  }
  return next(req);
};

Additional security rules:

  • Use short-lived access tokens (15 min) with long-lived refresh tokens (7 days, stored in HttpOnly cookie by backend)
  • Never log tokens in the console or error tracking tools
  • Use HTTPS only in production
  • Implement token rotation — issue a new refresh token on each use
  • Always validate tokens server-side — never trust the client

Last updated: May 2026