Angular Interview Questions

Common Angular interview questions covering dependency injection, Observables, routing, forms, and testing.

A curated set of Angular interview questions, ordered roughly from fundamentals to more advanced — the kind you'll actually be asked in real screens and on-sites.

Core concepts

Q: How does Angular's philosophy differ from React's or Vue's? Angular is a full, opinionated framework: routing, an HTTP client, dependency injection, and forms handling are all built in and designed to work together, with the Angular CLI enforcing a consistent project structure. React and Vue are comparatively minimal UI libraries — you assemble your own stack by choosing a router, an HTTP client, and a state management approach separately. Angular's structure costs more upfront learning (and is essentially built around TypeScript), but gives large teams a shared, consistent architecture without having to agree on and integrate separate libraries themselves.

Q: What is a standalone component, and how does it relate to NgModules? A standalone component (standalone: true) declares its own dependencies directly — the other components, directives, and pipes it needs — without being registered inside an NgModule. Older Angular applications organize every component into an NgModule's declarations array, with shared dependencies imported at the module level instead of the component level. Standalone components are the current recommended default for new Angular code, since they reduce boilerplate and make a component's dependencies explicit and local rather than implicit through whatever module happened to declare it.

Q: What's the difference between the modern @if/@for control flow and the older *ngIf/*ngFor structural directives? Both control conditional rendering and lists in a template, but @if/@for is built directly into the Angular compiler (no import required) and was introduced as the new default, generally with better performance — @for in particular requires an explicit track expression for identifying items, similar in purpose to a React/Vue list key. *ngIf/*ngFor are directives that require importing CommonModule (or the specific directive) into a standalone component, and remain extremely common in existing production codebases, so recognizing them is still necessary even in new projects.

Dependency injection

Q: Explain Angular's dependency injection model in your own words. A class (a component or another service) declares what it needs — typically another service — either as a constructor parameter or via the inject() function, rather than constructing that dependency itself with new. Angular's injector is responsible for resolving that request: creating the dependency if it doesn't exist yet, or handing back an existing instance if one is already available and the provider scope calls for reuse. This decouples a class from how its dependencies are constructed, which is what makes swapping a real service for a fake one in tests straightforward — you provide a different implementation for the same injection token without touching the consuming class's code.

Q: What does providedIn: 'root' do? It registers a service with Angular's root injector and marks it as an application-wide singleton — one single instance is created lazily, the first time anything actually requests it, and that same instance is shared by every part of the app that injects it afterward. This is the standard way to provide a service today, replacing the older pattern of listing a service in an NgModule's providers array.

RxJS and Observables

Q: What's the fundamental difference between a Promise and an Observable? A Promise represents exactly one asynchronous value (or one rejection) and begins executing immediately once created. An Observable represents a stream that can emit zero, one, many, or infinitely many values over time, only starts executing once something calls .subscribe() on it (it's "lazy"), and — unlike a Promise — can be cancelled mid-stream via .unsubscribe(). Angular's HttpClient returns Observables specifically because it's built on RxJS throughout, though for a single HTTP call that behaves much like a one-shot Promise in practice, the cancellability and composability (via RxJS operators) are the real reasons Observables are used instead.

Q: Why is the async pipe considered the idiomatic way to consume an Observable in an Angular template, instead of subscribing manually in the component class? Subscribing manually requires storing the Subscription and calling .unsubscribe() in the ngOnDestroy lifecycle hook yourself, or risking a memory leak if the component is destroyed while still subscribed — easy to forget, and it's a very common real-world bug. The async pipe (observable$ | async) handles subscribing, re-rendering on each new emitted value, and unsubscribing automatically when the component is destroyed, all declared directly in the template with no lifecycle management code in the component class at all.

Routing, forms, and testing

Q: What does a route guard like canActivate actually do, and how does the modern function-based guard style work? A route guard runs before Angular activates a matched route, deciding whether the navigation should proceed, redirect elsewhere, or be blocked entirely — commonly used to protect a route that requires an authenticated user. The modern style is a plain function (CanActivateFn) that calls inject() to reach whatever services it needs (an AuthService, the Router), returning true to allow navigation or a UrlTree (built with router.createUrlTree(...)) to redirect elsewhere instead — the same inject()-based dependency injection pattern used throughout components and services, not something guard-specific.

Q: Why does Angular provide two separate form APIs — template-driven and reactive — instead of one? They target different situations well. Template-driven forms (FormsModule, [(ngModel)]) keep the form's state in the template with minimal setup, a good fit for something as simple as a single search field. Reactive forms (ReactiveFormsModule, FormGroup/FormControl) define the form model explicitly in the component class instead, which makes validation logic, dynamic fields, and testing (the form model is a plain object, checkable without rendering anything) far more manageable once a form has real complexity — cross-field validation, conditionally shown fields, or non-trivial submit logic.

Q: In a reactive form, why check both .invalid and .touched before showing a validation error message? .invalid alone is true for an empty required field from the very first render, before the visitor has had any chance to interact with it — showing an error immediately would flag every required field as wrong the instant the page loads. .touched only becomes true once a control has been focused and blurred at least once, so combining field.invalid && field.touched shows an error only after the visitor has actually had a chance to fill the field in and moved on, which is the standard, less jarring pattern for surfacing validation feedback.

Q: Why does fixture.detectChanges() need to be called again after triggering a state change inside an Angular component test? In a running application, Angular's change detection runs automatically whenever something could have changed — an event handler firing, an Observable emitting, a timer completing. Inside TestBed's testing environment, that automatic cycle doesn't run on its own; a test has to call fixture.detectChanges() explicitly after any action that changes the component's state (a simulated click, or directly setting a property on fixture.componentInstance) before asserting on the rendered DOM, or the assertion sees stale, pre-change markup.