Angular Interview Questions and Answers
These questions cover the depth expected in frontend and full-stack developer interviews — from core framework concepts to Angular 17’s signals-based reactivity and modern performance patterns.
Core Concepts
Q1. What is Angular and how does it differ from React and Vue?
Angular is a full-featured, opinionated TypeScript framework by Google for building enterprise-scale web applications. It includes everything out of the box: routing, forms, HTTP client, dependency injection, and state management patterns.
Key differences:
| Angular | React | Vue | |
|---|---|---|---|
| Type | Full framework | UI library | Progressive framework |
| Language | TypeScript (required) | JavaScript/TypeScript | JavaScript/TypeScript |
| Data binding | Two-way | One-way | Two-way (v-model) |
| Learning curve | Steep | Moderate | Gentle |
| Architecture | Component + Service + Module | Component-centric | Component-centric |
| Reactivity (2025) | Signals + Zone.js | React hooks | Reactivity system |
Angular’s opinionated structure benefits large teams — everyone writes Angular the same way.
Q2. Explain Angular’s component architecture.
A component is the fundamental building block:
import { Component, signal, computed } from '@angular/core';
@Component({ selector: 'app-user-card', standalone: true, // Angular 14+ standalone (no NgModule needed) template: ` <div class="card"> <h2>{{ fullName() }}</h2> <p>{{ email }}</p> <button (click)="updateName()">Update</button> </div> `, styles: [`h2 { color: navy; }`]})export class UserCardComponent { firstName = signal('Alice'); lastName = signal('Chen'); email = 'alice@example.com';
fullName = computed(() => `${this.firstName()} ${this.lastName()}`);
updateName() { this.firstName.set('Alicia'); }}Each component has:
- Template — HTML with Angular bindings
- Class — TypeScript logic
- Styles — scoped CSS
- Metadata via
@Componentdecorator
Q3. What are Angular Signals and how do they improve reactivity?
Signals (introduced in Angular 16, stable in Angular 17) are a new reactive primitive that replace many Zone.js-based change detection patterns:
import { signal, computed, effect } from '@angular/core';
// Writable signalconst count = signal(0);count.set(5); // set new valuecount.update(n => n + 1); // update from current
// Computed signal (auto-tracks dependencies)const doubled = computed(() => count() * 2);
// Effect (runs whenever signals it reads change)effect(() => { console.log(`Count changed to: ${count()}`);});Benefits over Zone.js + Observables:
- Fine-grained reactivity — only components that read a changed signal re-render
- Synchronous — no async complexity for simple state
- No need for
asyncpipe in many cases - Better tree-shaking — can opt out of Zone.js entirely
- Easier to reason about — no need to manage subscriptions
Signals and RxJS are interoperable via toSignal() and toObservable().
Q4. What is Angular’s dependency injection system?
DI allows services to be injected into components without constructing them manually. Angular uses a hierarchical injector tree:
// Service definition@Injectable({ providedIn: 'root' // Singleton, available app-wide})export class UserService { private http = inject(HttpClient);
getUsers() { return this.http.get<User[]>('/api/users'); }}
// Injection in a component@Component({ ... })export class UsersComponent { private userService = inject(UserService); // Modern inject() function // OR constructor injection: // constructor(private userService: UserService) {}}DI scopes:
providedIn: 'root'— singleton across the appprovidersin@Component— new instance per component (and its children)providersin a route — scoped to the route’s component tree
Q5. Explain change detection in Angular and what OnPush does.
By default, Angular’s change detection (Zone.js) checks every component in the tree after any async event (user click, HTTP response, setTimeout).
ChangeDetectionStrategy.OnPush tells Angular to only check a component when:
- An
@Inputreference changes - An event in the component fires
- Observables used with
asyncpipe emit - You call
markForCheck()ordetectChanges()manually - A signal it reads changes (Angular 17+)
@Component({ selector: 'app-product', changeDetection: ChangeDetectionStrategy.OnPush, template: `{{ product().name }}`})export class ProductComponent { product = input.required<Product>(); // New signal-based input (Angular 17)}OnPush + signals = near-zero unnecessary re-renders. Always use OnPush for performance-sensitive components.
RxJS & Async
Q6. What are the key RxJS operators used in Angular and when do you use them?
import { HttpClient } from '@angular/common/http';import { switchMap, debounceTime, distinctUntilChanged, catchError, takeUntilDestroyed } from 'rxjs/operators';import { of } from 'rxjs';
@Component({ ... })export class SearchComponent { private http = inject(HttpClient); searchResults$ = this.searchInput.valueChanges.pipe( debounceTime(300), // Wait 300ms after user stops typing distinctUntilChanged(), // Only emit if value actually changed switchMap(query => // Cancel previous request if new one arrives this.http.get<Result[]>(`/api/search?q=${query}`).pipe( catchError(() => of([])) // Handle errors gracefully ) ), takeUntilDestroyed() // Auto-unsubscribe when component destroys );}Key operators:
switchMap— cancel previous, switch to new (search autocomplete)concatMap— queue sequentially (sequential HTTP calls)mergeMap— run all concurrently (parallel HTTP calls)exhaustMap— ignore new until current completes (submit button)combineLatest— combine latest from multiple streamsforkJoin— wait for all observables to complete (parallel requests, all must complete)
Q7. What is the async pipe and why is it preferred over manual subscriptions?
The async pipe subscribes to an Observable or Promise in the template and automatically unsubscribes when the component is destroyed:
// Componentresults$ = this.http.get<Item[]>('/api/items');
// Template@if (results$ | async; as results) { @for (item of results; track item.id) { <app-item [data]="item" /> }} @else { <p>Loading...</p>}Benefits:
- No manual
subscribe()/unsubscribe()code - No memory leaks
- Handles loading state (null until emission)
- Works with
OnPushchange detection
The @if / @for block syntax (Angular 17) replaces *ngIf / *ngFor structural directives.
Routing
Q8. How does lazy loading work in Angular?
Lazy loading delays loading a module or component’s code until the user navigates to that route:
import { Routes } from '@angular/router';
export const routes: Routes = [ { path: 'admin', loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES) }, { path: 'dashboard', loadComponent: () => import('./dashboard/dashboard.component') .then(c => c.DashboardComponent) // Standalone component lazy loading }];With standalone components (Angular 14+), you can lazy-load individual components without creating feature modules. This reduces initial bundle size and improves Time to Interactive.
Q9. What are route guards and what types are available?
Route guards control navigation based on conditions:
// Modern functional guard (Angular 15+)export const authGuard: CanActivateFn = (route, state) => { const auth = inject(AuthService); return auth.isLoggedIn() ? true : inject(Router).createUrlTree(['/login']);};
// Apply to routeconst routes: Routes = [ { path: 'profile', component: ProfileComponent, canActivate: [authGuard] }];Guard types:
CanActivate— can the user enter this route?CanActivateChild— can the user enter child routes?CanDeactivate— can the user leave this route? (unsaved changes warning)CanMatch— can this route even be matched? (feature flags, roles)Resolve— pre-fetch data before the component activates
Forms
Q10. What is the difference between template-driven and reactive forms?
Template-driven forms — logic lives in the template. Simple forms, less boilerplate.
<form #form="ngForm" (ngSubmit)="onSubmit(form)"> <input name="email" [(ngModel)]="email" required email> <button type="submit">Submit</button></form>Reactive forms — logic lives in the component class. Better for complex validation, dynamic fields, and unit testing.
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
form = inject(FormBuilder).group({ email: ['', [Validators.required, Validators.email]], password: ['', [Validators.required, Validators.minLength(8)]]});
get emailControl() { return this.form.get('email'); }<form [formGroup]="form" (ngSubmit)="onSubmit()"> <input formControlName="email"> @if (emailControl?.invalid && emailControl?.touched) { <p>Valid email required</p> }</form>Reactive forms are preferred for enterprise applications — they’re testable, composable with custom validators, and integrate naturally with RxJS.
Performance
Q11. What strategies improve Angular application performance?
Build-time:
- Enable production build (
ng build --configuration production) — tree-shaking, minification - Lazy load all routes that aren’t part of the critical path
- Use
deferblocks (Angular 17) to defer non-critical component rendering
Runtime:
- Use
OnPush+ signals everywhere possible - Use
trackBy/trackin lists to avoid unnecessary DOM operations - Use Virtual Scrolling (
CdkVirtualScrollViewport) for long lists - Preload strategies:
PreloadAllModulesor custom predictive preloading
Network:
- Serve from CDN, enable HTTP/2, compress assets
- Use
transferStatefor SSR to avoid double HTTP calls - Image optimization with
NgOptimizedImagedirective
Deferrable Views (Angular 17):
@defer (on viewport) { <app-heavy-chart />} @placeholder { <div class="skeleton"></div>}Q12. What is Angular Universal (SSR) and what problems does it solve?
Angular Universal renders Angular apps on the server, sending HTML to the browser before JavaScript executes:
Benefits:
- SEO — search engine crawlers see full content immediately
- Core Web Vitals — better FCP (First Contentful Paint) and LCP
- Social sharing — Open Graph meta tags rendered server-side
Angular 17 ships with SSR and SSG (Static Site Generation) built into the CLI by default.
ng new my-app --ssr # Create new app with SSRHydration (reusing server-rendered DOM instead of discarding it) became stable in Angular 17, eliminating the “flash of unstyled content” and improving performance significantly.
Q13. What are the key new features in Angular 17 (2023) and what’s expected in Angular 18/19?
Angular 17 (2023):
- New control flow syntax:
@if,@for,@switch,@defer(replaces*ngIf,*ngFor,NgSwitch) - Signals API stable for production use
- SSR and hydration stable, SSR-first scaffolding by default
input(),output(),viewChild(),model()signal-based component inputs/outputs- Vite + esbuild-based builder (much faster builds)
Angular 18 (2024):
- Zoneless change detection (experimental) — eliminate Zone.js dependency with pure signals
- Material Design 3 components
- Stable
afterRenderEffectfor DOM measurement
Angular 19 (2024-2025):
- Stable incremental hydration (defer blocks + partial hydration)
- Stable linkedSignal and resource API for async signal patterns
- Hot module replacement improvements
Quick Reference: Key Decorators
| Decorator | Purpose |
|---|---|
@Component | Defines a component |
@Injectable | Marks a class as a DI service |
@NgModule | Groups related declarations (being replaced by standalone) |
@Input / input() | Receives data from parent |
@Output / output() | Emits events to parent |
@ViewChild | Gets reference to a child component or DOM element |
@HostListener | Listens to DOM events on the host element |
@Pipe | Creates a template transformation pipe |