Angular developer interviews in 2026 go far beyond “what is a component.” Hiring teams expect signals and zoneless change detection, RxJS operator choice (switchMap vs mergeMap), standalone architecture, and senior judgment on when NgRx is worth the ceremony—not AngularJS trivia from a decade ago.
Below are 40+ Angular interview questions and preparation topics for Angular developer and senior Angular developer loops, covering fundamentals through production architecture and scenario design. Open each answer after you try the question yourself. For TypeScript interview questions (generics, guards, utility types), see TypeScript interview questions. For React JS interview questions, see React interview questions and answers and React JS interview questions for experienced developers. For dedicated CSS interview questions (flexbox, grid, cascade), see CSS interview questions. For browser-wide HTML, CSS, and general front end depth, see front end developer interview questions. For API and full-stack integration, see full stack developer interview questions.
Interview context and how to prepare
What do Angular developer interviews actually test?
Angular interviews test whether you can ship maintainable enterprise UIs on the modern Angular stack—not only scaffold CLI apps.
| Level | Typical focus |
|---|---|
| Junior | Components, templates, *ngIf/*ngFor, basic services, HTTP |
| Mid | RxJS, reactive forms, routing, lazy loading, unit tests |
| Senior | Change detection, signals, architecture, performance, NgRx trade-offs, migration |
| Staff+ | Monorepo boundaries, design systems, platform APIs, team conventions |
Common formats:
- Live coding (component + service + HTTP)
- Take-home feature with tests
- Architecture whiteboard (dashboard, auth, feature modules)
- Senior deep dives on RxJS and change detection
A strong candidate explains trade-offs with shipped examples—not memorized definition lists.
A strong answer is:
These interviews test whether I can apply the topic under pressure—with clear mechanisms, trade-offs, and production judgment—not encyclopedic trivia.
What extra bar do senior Angular interviews add?
Mid-level loops check if you can build features. Senior loops check if you can own them at scale.
| Area | Mid signal | Senior signal |
|---|---|---|
| State | Service + BehaviorSubject | Signals + NgRx when justified |
| RxJS | subscribe in component |
Operator choice, leak prevention, error strategy |
| CD | Knows OnPush exists | Zoneless, markForCheck, signal consumers |
| Architecture | Feature folders | Standalone boundaries, lazy routes, library APIs |
| Quality | Some unit tests | Testing strategy, a11y, bundle budgets |
Interviewers cite production incidents—memory leaks, slow tables, duplicate HTTP from wrong switchMap—and ask how you prevented recurrence.
A strong answer is:
“Senior Angular interviews go beyond building a feature. They test whether I can make architecture and state-management decisions, diagnose performance and RxJS problems, migrate legacy code safely, and explain the trade-offs behind those choices.”
What is a typical Angular developer interview loop?
| Round | Duration | Focus |
|---|---|---|
| Recruiter / HM | 30 min | Projects, Angular version shipped, team size |
| TypeScript + Angular fundamentals | 45–60 min | Components, DI, templates, HTTP |
| RxJS + state | 45–60 min | Operators, forms, async pipe, signals |
| Live coding | 45–90 min | List + detail, search, form validation |
| System design | 45–60 min | Large SPA, auth, micro-frontends—senior |
| Behavioral | 30 min | Deadlines, refactors, mentoring |
Partners and product companies often use HackerRank/CoderPad for TypeScript before framework depth.
Read the JD for Angular version. In 2026, Angular 22.x is current (released June 2026); Angular 21 made zoneless the default and Angular 22 made OnPush the default change-detection strategy. Angular 20 and 21 remain supported.
A strong answer is:
I expect fundamentals, live coding or scenarios, and depth follow-ups—so I prepare stories and small examples for each major topic.
What is a realistic 4–6 week prep plan?
| Week | Focus | Output |
|---|---|---|
| 1 | TypeScript — generics, unions, strict mode | 15 small typing exercises |
| 2 | Components, standalone, templates, pipes | Rebuild one screen standalone |
| 3 | RxJS — switchMap, mergeMap, exhaustMap, async pipe |
Typeahead + button-submit labs |
| 4 | Signals + change detection + OnPush | Migrate one component to signals |
| 5 | Routing, guards, lazy load, reactive forms | Mini app with auth guard |
| 6 | Testing + scenarios — Vitest/Jest, architecture STAR | One whiteboard + three stories |
Pair with front end interviews for CSS, a11y, and Core Web Vitals vocabulary.
A strong answer is:
I'd build hands-on drills each week, explain answers aloud, and close every technical card with one sentence I could say in the room.
Components, standalone architecture, and templates
What is an Angular component?
What interviewers are testing: Whether you understand the component as a clear UI boundary: what it owns, how Angular renders it, how dependencies enter it, and how it communicates with its parent.
An Angular component is the basic building block of the UI. It combines:
- A template that defines what the user sees
- A class that holds state, behavior, and dependencies
@Componentmetadata such asselector,templateUrl,styleUrl, andimports- Inputs and outputs for communication with parent components
@Component({
selector: 'app-user-card',
standalone: true,
imports: [CommonModule],
template: `<h2>{{ user().name }}</h2>`,
})
export class UserCardComponent {
user = input.required<User>();
}In modern Angular, components are usually standalone, so their template dependencies are listed directly in imports instead of being declared through an NgModule.
A good interview answer should also mention component design:
- Presentational components receive data through inputs and emit events
- Smart/container components coordinate services, routing, API calls, and state
- HTTP logic should normally live in services, not directly inside reusable UI components
A strong answer is:
“A component owns one part of the UI. The class holds its state and behavior, the template renders that state, and inputs and outputs define its public API. In modern Angular, standalone components also declare their template dependencies directly.”
What are standalone components and why are they default now?
What interviewers are testing: Whether you understand why Angular moved away from requiring NgModules for ordinary component composition, and how standalone APIs simplify dependency visibility, lazy loading, and incremental migration.
Standalone components do not need to be declared in an NgModule. They import their own dependencies directly:
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [UserCardComponent, RouterLink],
templateUrl: './dashboard.html',
})
export class DashboardComponent {}| Older NgModule style | Standalone style |
|---|---|
Components declared in declarations |
Components import dependencies directly |
| Shared modules often re-export many things | Dependencies are visible in the component |
Lazy loading commonly used loadChildren |
Routes can use loadComponent |
| More boilerplate | Smaller, clearer dependency graph |
Standalone components are preferred because they make Angular apps:
- Easier to lazy load
- Clearer about dependencies at the component boundary
- Easier to migrate gradually
- Easier for new developers to understand
- Less dependent on large shared modules
Legacy NgModule-based apps still exist, so interviewers may expect you to understand both styles.
A strong answer is:
“Standalone components remove unnecessary NgModule boilerplate. Each component imports what it uses, which makes dependencies clearer and supports simpler lazy loading and migration.”
input(), output(), and @Input/@Output — what changed?
What interviewers are testing: Whether you know the modern component API while still being able to maintain decorator-based Angular code, and whether you understand the different roles of inputs, outputs, and two-way model bindings.
Modern Angular provides function-based component APIs such as signal inputs with input(), output() for custom events, and model() for two-way binding.
Modern style:
count = input(0);
save = output<Item>();Older decorator style:
@Input() count = 0;
@Output() save = new EventEmitter<Item>();The newer input() API gives you a signal-like value that works well with computed() and template reactivity.
Example:
name = input.required<string>();
displayName = computed(() => this.name().trim().toUpperCase());model() is used when a component needs two-way binding:
checked = model(false);This automatically supports a matching change output, such as checkedChange.
Important interview points:
@Input()and@Output()are still common in existing appsinput()works better with signals and modern Angular patternsoutput()is the modern function-based output API and avoids manually creating anEventEmitterfor many new components.@Output()+EventEmitterremains common in existing applications.model()is useful for custom form-like components- Outputs should notify parents about UI events, not contain complex business logic
A strong answer is:
“The older decorators still work, but modern Angular provides
input(),output(), andmodel()as function-based component APIs.input()andmodel()integrate naturally with signals, whileoutput()provides a cleaner API for emitting custom events.”
Which lifecycle hooks still matter in 2026?
What interviewers are testing: Whether you know which work belongs in lifecycle hooks versus signals, render callbacks, or automatic cleanup APIs—and whether you avoid using hooks as a catch-all for state synchronization.
The most important lifecycle hooks are still useful, but signals reduce the need for some older patterns.
| Hook / API | When to use |
|---|---|
ngOnInit |
One-time initialization after Angular creates the component |
ngOnChanges |
Reacting to changes in decorator-based @Input() values |
ngAfterViewInit |
Accessing view children after the view is initialized |
ngOnDestroy |
Cleanup for timers, subscriptions, and external resources |
afterNextRender() |
Run DOM-related work once after Angular renders |
afterEveryRender() |
Run DOM-related work after every render |
Modern Angular patterns:
- Use
computed()for derived state instead of manually recalculating in lifecycle hooks - Use
effect()carefully for side effects - Use
takeUntilDestroyed()instead of manual unsubscribe subjects - Use
afterNextRender()for DOM work like charts, focus, or non-Angular libraries
Avoid doing heavy work in the constructor. The constructor should mainly inject dependencies.
A strong answer is:
“Lifecycle hooks still matter, but I avoid using them for derived state. I use signals and
computed()for state,ngOnInitfor initialization,ngOnDestroyfor cleanup, and render callbacks for DOM-dependent work.”
Why are pipes preferred over template functions?
What interviewers are testing: Whether you understand that template expressions participate in change detection and can recognize expensive work that should be moved into a pure pipe or precomputed state.
Pipes are usually better than calling functions directly from templates because Angular can optimize pure pipes.
<!-- Prefer -->
{{ price | currency:'USD' }}
<!-- Avoid for expensive work -->
{{ formatPrice(price) }}A pure pipe is reevaluated when Angular detects a change to its primitive input or object/reference input, rather than on every relevant template evaluation. A template function can be called repeatedly during change detection, which becomes expensive in large lists or frequently updated screens.
This is especially risky when the function:
- Performs calculations
- Filters or sorts arrays
- Creates new objects or arrays
- Calls another service method
- Runs inside
@for/*ngFor
The async pipe is also important because it:
- Subscribes to an observable
- Updates the view when a new value arrives
- Unsubscribes automatically when the component is destroyed
- Marks the component for checking when needed
Use a template function only when it is simple, cheap, and does not create new references.
A strong answer is:
“Pure pipes avoid repeatedly executing expensive template functions when their inputs have not changed. For derived application state I may also use
computed(), whileasyncpipe is useful for template-bound observables.”
Structural vs attribute directives?
What interviewers are testing: Whether you understand how directives modify existing elements or template structure, and whether you can relate legacy *ngIf/*ngFor code to modern @if/@for syntax.
Angular has two major directive categories.
| Type | Examples | What it does |
|---|---|---|
| Structural directive | *ngIf, *ngFor, *ngSwitch |
Adds, removes, or repeats DOM views |
| Attribute directive | ngClass, ngStyle, custom appHighlight |
Changes behavior, style, or properties of an existing element |
Example structural directive:
<div *ngIf="isLoggedIn">Welcome</div>Example attribute directive:
<div [ngClass]="{ active: isSelected }">User</div>Modern Angular also has built-in control flow:
@if (isLoggedIn) {
<p>Welcome</p>
}
@for (user of users; track user.id) {
<app-user-card [user]="user" />
}@if, @for, and @switch are now preferred in new code because they are clearer and optimized by the Angular compiler.
@defer is another important modern feature. It lets Angular delay loading part of the UI until a trigger such as viewport, idle time, interaction, or hover.
A strong answer is:
“Structural directives change the DOM structure, while attribute directives change the behavior or appearance of an existing element. In modern Angular, I prefer the new
@ifand@forcontrol flow for new templates.”
Change detection, Zone.js, and signals
How does Angular change detection work?
What interviewers are testing: Whether you understand what schedules a view update, how OnPush changes traversal, and why signals and zoneless Angular make change notifications more explicit.
Angular change detection keeps the DOM in sync with component state.
In older zone-based Angular apps, the common flow is:
- A browser event, timer, promise, or HTTP request completes
- Zone.js notifies Angular
- Angular checks component bindings
- Angular updates the DOM where values changed
In newer zoneless Angular apps, Angular does not rely on Zone.js to know that something changed. Instead, change detection is triggered by clear notifications such as:
- Signal updates
- Template events
asyncpipe emissionsmarkForCheck()- Framework APIs that notify Angular
Change detection strategy also matters:
| Strategy | Behavior |
|---|---|
| Eager (formerly Default) | Angular checks more broadly when activity happens. In Angular 22, ChangeDetectionStrategy.Eager is the explicit name; Default remains an alias but is deprecated as an enum member |
| OnPush | Angular checks a component when inputs change, events happen, signals update, or it is marked for check. Default in Angular 22 |
Interviewers usually want to know both the old and modern model.
For performance-heavy screens, such as tables, dashboards, and charts, reduce unnecessary work by using:
- Signals and
computed()for predictable reactive state - Stable
trackexpressions with@for - Pure pipes or precomputed values instead of expensive template work
- Pagination or virtual scrolling for large datasets
- Lazy loading and
@deferfor heavy features - Profiling to identify legacy Eager components or unnecessary checks
A strong answer is:
“Angular change detection synchronizes component state with template bindings. In older zone-based apps, Zone.js helped schedule checks after async activity; modern Angular increasingly relies on explicit notifications such as signals, events, AsyncPipe, and
markForCheck(). OnPush then lets Angular skip subtrees that don't need checking.”
How does OnPush change detection work, and what changes now that it is the default?
What interviewers are testing: Whether you understand when an OnPush view becomes eligible for checking and can diagnose stale UI caused by mutations or missing change notifications.
OnPush tells Angular to skip unnecessary checks and only re-check a component when Angular has a clear reason.
Angular 22 makes OnPush the default change-detection strategy. Older applications may still explicitly use ChangeDetectionStrategy.Default (now represented as Eager). In legacy projects you often enabled OnPush manually:
@Component({
selector: 'app-user-list',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './user-list.html',
})
export class UserListComponent {}OnPush is not just “immutable inputs.” A view becomes eligible for checking when Angular receives a relevant notification—input binding changes, template events, signal updates, async pipe emissions, or markForCheck(). Mutating an object passed from a parent can still produce stale UI when no notification occurs.
OnPush views are easiest to reason about when state changes are exposed through:
- Inputs
- Signals
- Observables with
asyncpipe - Immutable object updates
- Local template events
Common mistake:
// Mutates the same object reference
this.user.name = 'New name';Better:
// Creates a new reference
this.user = { ...this.user, name: 'New name' };With signals, you update state through signal APIs:
user.update(value => ({ ...value, name: 'New name' }));In Angular 22, the practical concern is no longer deciding where to enable OnPush. Instead, make sure state changes use Angular-recognized notification mechanisms and understand legacy Eager components when maintaining older applications.
Be careful when a component depends on mutable singleton state that changes without signals, observables, or markForCheck().
A strong answer is:
“OnPush lets Angular skip a component subtree until it receives a relevant notification, such as an input change, template event, signal update, or
markForCheck(). Since Angular 22 uses OnPush by default, I focus less on enabling it and more on keeping state updates predictable so Angular knows when the view needs refreshing.”
What is zoneless Angular?
What interviewers are testing: Whether you understand what Zone.js historically did, what now schedules change detection without it, and what kinds of legacy code or third-party integrations can break during migration.
Zoneless Angular means Angular does not depend on Zone.js to schedule change detection.
In a zone-based app, Angular often runs change detection after async browser activity because Zone.js patches APIs like events, timers, and promises.
Angular 21+ is zoneless by default. In Angular 20, you enabled zoneless explicitly with provideZonelessChangeDetection().
In a zoneless app, Angular updates the UI when there is an explicit signal that something changed, such as:
- A signal value changes
- A template event runs
- An observable bound with
asyncemits ChangeDetectorRef.markForCheck()is called- Angular framework APIs schedule an update
Typical bootstrap configuration (Angular 20—explicit opt-in):
bootstrapApplication(AppComponent, {
providers: [
provideZonelessChangeDetection(), // Angular 21+: not needed; zoneless is default
],
});Why teams like zoneless:
- Smaller runtime dependency
- Fewer unexpected change detection cycles
- Better performance control
- Better fit with signals
- Easier debugging of “what caused this render?”
Migration concerns:
- Older third-party libraries may assume Zone.js exists
- Code that changes template-visible state without using one of Angular's notification mechanisms must ensure Angular is notified
- Existing manual
NgZone.run()usage needs review - OnPush-compatible components migrate more easily
A strong answer is:
“Zoneless Angular removes the dependency on Zone.js for scheduling change detection. It works best when state changes are visible through signals, async pipe, events, or explicit
markForCheck()calls.”
signal(), computed(), and effect() — explain each.
What interviewers are testing: Whether you can distinguish source state, derived state, and side effects—and avoid using effect() to propagate state that should instead be expressed with computed().
Angular signals are reactive values that tell Angular where state is used and when it changes.
const count = signal(0);
const double = computed(() => count() * 2);
effect(() => {
console.log('count changed:', count());
});| Primitive | Meaning | Best use |
|---|---|---|
signal() |
Writable reactive value | Local UI state |
computed() |
Derived read-only value | View-model calculations |
effect() |
Side effect when dependencies change | Logging, local storage, external APIs |
Example:
firstName = signal('Deepak');
lastName = signal('Prasad');
fullName = computed(() => `${this.firstName()} ${this.lastName()}`);Use computed() when a value can be derived from other state. Do not store duplicate state manually if Angular can derive it.
Use effect() carefully. It is not the best place for normal business logic or state propagation. It is better for side effects such as:
- Logging
- Syncing to local storage
- Calling non-Angular APIs
- Updating a chart library
Important interview detail: signals are read by calling them as functions.
count(); // read signal value
A strong answer is:
“
signal()stores reactive state,computed()derives values from other signals, andeffect()runs side effects. In most UI code, I prefercomputed()for derived state and useeffect()only when I need to interact with something outside normal template rendering.”
Signals vs RxJS — when do you use each?
What interviewers are testing: Whether you avoid the false choice of “signals replace RxJS” and can choose between state reactivity and asynchronous stream composition.
Signals and RxJS solve related but different problems.
| Signals | RxJS Observables |
|---|---|
| Best suited to current reactive state | Best suited to streams/events over time |
| Synchronous | Can be async, cancellable, and composable |
| Great for UI state | Great for HTTP, websockets, events, streams |
Read with signal() |
Subscribe manually or use async pipe |
| Works naturally with templates | Strong operators for async workflows |
Use signals for:
- Component state
- Derived view-models
- Form UI state
- Toggle/filter/sort state
- Values shown directly in templates
Use RxJS for:
- HTTP request pipelines
- Websocket streams
- Debounced search
- Retry/cancel/error flows
- Combining multiple async sources
- Complex event streams
Bridge APIs:
user = toSignal(this.userService.user$);
user$ = toObservable(this.user);Modern Angular also has resource APIs for async signal-based data loading, such as resource(), rxResource(), and httpResource().
A practical pattern:
- Keep service-level async flows in RxJS when operators are useful
- Convert observables to signals near the component/template boundary
- Use signals for derived UI state
- Avoid replacing every observable just because signals exist
A strong answer is:
“RxJS is still excellent for async streams and operators. Signals are better for current UI state and derived template values. I usually let streams feed the system and use signals to feed the template.”
markForCheck() vs detach() on ChangeDetectorRef?
What interviewers are testing: Whether you understand the difference between marking a view for a future change-detection pass and removing that view from normal change-detection traversal entirely.
Both APIs are used for manual change detection control, but they solve different problems.
| API | What it does | When to use |
|---|---|---|
markForCheck() |
Marks the view and its ancestors so Angular includes it in a future change-detection pass | External async updates, manual integrations |
detach() |
Removes a view from automatic change detection | Advanced high-frequency rendering control |
reattach() |
Adds the view back to automatic checking | Resume normal Angular updates |
detectChanges() |
Immediately checks the current view and children | Manual checking after detach |
Example use of markForCheck():
constructor(private cdr: ChangeDetectorRef) {}
loadData() {
someExternalApi((value) => {
this.value = value;
this.cdr.markForCheck();
});
}Use markForCheck() when Angular would not otherwise know that something changed.
Use detach() only for advanced cases, such as:
- Very high-frequency chart updates
- Large read-only lists updated in batches
- Manual rendering loops
- Performance-sensitive dashboards
Be careful: if you detach a component and forget to call detectChanges() or reattach(), the UI can become stale.
With signals and async pipe, you usually need less manual ChangeDetectorRef code because Angular receives clearer change notifications.
A strong answer is:
“
markForCheck()marks a view so Angular checks it in a future change-detection pass.detach()removes the view from normal checking, so I must usedetectChanges()orreattach()when updates are required.”
RxJS and async patterns
What is an Observable and how does Angular use RxJS?
What interviewers are testing: Whether you understand Observable execution, subscription lifecycle, cold HTTP streams, and why multiple subscriptions can accidentally duplicate work.
An Observable represents a stream of values over time. A subscriber reacts when the observable emits a value, throws an error, or completes.
Angular uses RxJS in many places:
HttpClientrequests- Router events and route params
- Reactive forms
valueChangesandstatusChanges - Websocket/event streams
- Interop with signals through
toSignal()andtoObservable()
Important interview difference:
| Promise | Observable |
|---|---|
| Usually one value | Can emit zero, one, or many values |
| Starts immediately once created | Often lazy until subscribed |
Basic chaining with then() |
Rich operators like map, switchMap, retry |
| Cancellation is not built in directly | An Observable subscription can be unsubscribed, and producers that support teardown—such as Angular HttpClient—can cancel the underlying work |
HTTP observables from HttpClient are usually cold. That means each subscription can trigger a new HTTP request unless the stream is shared.
const users$ = this.http.get<User[]>('/api/users');
// Two subscriptions may create two HTTP requests
users$.subscribe();
users$.subscribe();For one-time template reads, use async pipe. For shared/cached streams, use a service-level strategy such as shareReplay, cache invalidation, or a signal/resource-based store.
A strong answer is:
“An Observable is a lazy stream that can emit multiple values over time. Angular uses RxJS for HTTP, routing, forms, and async UI flows. Subscriptions can be torn down, and cold HTTP observables can trigger duplicate requests when subscribed multiple times.”
switchMap vs mergeMap vs concatMap vs exhaustMap?
What interviewers are testing: Whether you choose flattening operators based on concurrency semantics—cancel, parallelize, queue, or ignore—not from memorized operator names.
These are higher-order mapping operators. They map each source value to an inner observable, but they handle overlapping inner subscriptions differently.
| Operator | Behavior | Best use |
|---|---|---|
switchMap |
Cancels previous inner observable when a new value arrives | Typeahead search, route-param reload |
mergeMap |
Runs inner observables in parallel | Independent parallel requests |
concatMap |
Queues inner observables one after another | Ordered writes, sequential saves |
exhaustMap |
Ignores new values while current inner observable is active | Login/submit button double-click prevention |
Example: latest search term wins.
searchTerm$.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.api.search(term))
);Use the wrong operator and you can create real bugs:
mergeMapfor search can show stale resultsswitchMapfor writes can cancel a save requestconcatMapfor long tasks can create a backlogexhaustMapfor search can ignore the latest user input
A practical rule:
- Read latest data:
switchMap - Write in order:
concatMap - Run independent work:
mergeMap - Ignore duplicate clicks:
exhaustMap
A strong answer is:
“I choose the operator based on concurrency.
switchMapcancels old work,mergeMapruns parallel work,concatMappreserves order, andexhaustMapprevents duplicate submissions while one request is active.”
How do you avoid RxJS memory leaks?
What interviewers are testing: Whether you understand which streams actually outlive a component, how Angular-aware teardown works, and whether you can avoid nested or unmanaged subscriptions.
RxJS memory leaks usually happen when a component subscribes to a long-lived observable and does not unsubscribe when the component is destroyed.
Preferred patterns:
- Use the
asyncpipe in templates - Use
takeUntilDestroyed()for component subscriptions - Use
takeUntil(destroy$)in older Angular code - Avoid nested manual
subscribe()calls - Prefer services/facades for complex stream orchestration
Template approach:
@if (user$ | async; as user) {
<p>{{ user.name }}</p>
}Component approach:
private readonly destroyRef = inject(DestroyRef);
ngOnInit() {
this.form.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(value => {
this.filter.set(value);
});
}takeUntilDestroyed() without an explicit DestroyRef expects an injection context. Angular documents that it can be omitted in the constructor; outside one, pass DestroyRef.
Be careful with long-lived streams such as:
valueChanges- Router events
- Websocket streams
- DOM event streams
- Store selectors
- Subjects in services
HTTP requests usually complete after one response, but you should still avoid messy manual subscriptions when async pipe or toSignal() can express the same flow more cleanly.
Senior-level point: shareReplay() can prevent duplicate work, but its sharing and caching behavior must be chosen deliberately. Think about subscriber lifetime, refCount, stale data, and application-level cache invalidation instead of treating it as automatic caching.
A strong answer is:
“I avoid leaks by preferring
asyncpipe, usingtakeUntilDestroyed()for manual subscriptions, and keeping long-lived stream orchestration in services. I also useshareReplay()deliberately because subscriber lifetime, sharing behavior, and stale-data/cache policy can otherwise create subtle problems.”
Why prefer async pipe over manual subscribe?
What interviewers are testing: Whether you can keep observable consumption declarative and let Angular manage subscription lifecycle and view notification instead of manually mirroring stream values into component fields.
The async pipe is preferred because it handles subscription lifecycle automatically.
Benefits:
- Subscribes to the observable
- Renders the latest emitted value
- Marks the view for checking when a new value arrives
- Unsubscribes automatically when the component is destroyed
- Unsubscribes from the old stream if the observable reference changes
- Works well with
OnPush
@if (user$ | async; as user) {
<p>{{ user.name }}</p>
}Manual subscription adds boilerplate:
ngOnInit() {
this.sub = this.userService.user$.subscribe(user => {
this.user = user;
});
}
ngOnDestroy() {
this.sub.unsubscribe();
}Use manual subscribe only when you are doing side effects that do not belong directly in the template, such as:
- Updating a non-Angular chart library
- Calling browser APIs
- Logging analytics
- Coordinating imperative workflows
Even then, use takeUntilDestroyed() or another teardown pattern.
A strong answer is:
“I prefer
asyncpipe because it keeps the component class clean, works with OnPush, and handles subscribe/unsubscribe automatically. I manually subscribe only for side effects or orchestration that cannot be expressed in the template.”
How do you handle errors in HTTP streams?
What interviewers are testing: Whether you put errors at the correct layer, distinguish transient reads from unsafe writes, and know that an errored Observable terminates unless you replace/recover it.
HTTP error handling should happen at the right layer.
Common patterns:
- Use
catchError()for fallback values or mapped errors - Use
retry()only for transient failures - Use an
HttpInterceptorfor global concerns like auth errors - Show user-friendly errors through a toast, banner, or error signal
- Use
finalize()when loading state must reset on success or failure
users$ = this.http.get<User[]>('/api/users').pipe(
retry({ count: 2, delay: 500 }),
catchError(error => {
this.error.set('Unable to load users');
return of([]);
})
);Do not blindly retry everything. Retrying a failed GET may be fine for temporary network issues. Retrying a POST can create duplicate writes unless the backend is idempotent.
Also remember: when an observable errors, that stream ends unless catchError() replaces it with another observable.
Good separation:
| Layer | Responsibility |
|---|---|
| Interceptor | Auth token, 401 handling, correlation ID, global logging |
| Service | API-specific mapping, retry rules, fallback |
| Component | User-facing message and view state |
A strong answer is:
“I handle expected request errors close to the service, use interceptors for global HTTP concerns, retry only safe transient failures, and expose a clear loading/error state to the UI.”
Subject variants — when use each?
What interviewers are testing: Whether you understand replay/current-value semantics and can avoid turning Subjects into an unstructured application-wide event bus.
A Subject is both an observable and an observer. It can multicast values to multiple subscribers.
| Type | Behavior | Typical use |
|---|---|---|
Subject |
No initial value, no replay | Fire-and-forget events |
BehaviorSubject |
Requires initial value and emits latest value to new subscribers | Current state in older RxJS services |
ReplaySubject |
Replays N previous values to new subscribers | Event/history replay |
AsyncSubject |
Emits only the last value when completed | Rare; final result on completion |
Example older service pattern:
private readonly userSubject = new BehaviorSubject<User | null>(null);
readonly user$ = this.userSubject.asObservable();Modern Angular alternative for simple UI state:
private readonly user = signal<User | null>(null);
readonly currentUser = this.user.asReadonly();Use subjects carefully. They are useful, but overusing them can create hidden event buses that are hard to trace.
Prefer:
- Signals for simple current UI state
- RxJS streams for async workflows
- NgRx/SignalStore for larger shared state
- Explicit service methods instead of random public subjects
Avoid exposing writable subjects publicly.
// Avoid
userSubject = new BehaviorSubject(null);
// Prefer
private readonly userSubject = new BehaviorSubject<User | null>(null);
readonly user$ = this.userSubject.asObservable();A strong answer is:
“I use
Subjectfor events,BehaviorSubjectfor current-value state in RxJS services,ReplaySubjectwhen new subscribers need history, andAsyncSubjectrarely. In modern Angular, I often use signals instead of BehaviorSubject for simple UI state.”
Forms, routing, and HTTP
Reactive forms vs template-driven forms?
What interviewers are testing: Whether you choose a form model based on validation complexity, dynamic structure, type safety, and testability rather than personal preference.
Angular supports both reactive forms and template-driven forms.
| Area | Reactive forms | Template-driven forms |
|---|---|---|
| Form model | Explicit in TypeScript | Mostly created from template directives |
| Data flow | Synchronous and predictable | More template-driven |
| Testing | Easier to unit test | More dependent on template behavior |
| Dynamic forms | Strong choice | Awkward for complex cases |
| Validation | Validator functions | Validator directives |
| Best for | Enterprise/complex forms | Simple forms |
Use reactive forms for:
- Login/registration forms with validation
- Dynamic fields
- Cross-field validation
- Async validation
- Multi-step forms
- Enterprise forms with testing needs
Use template-driven forms for:
- Small contact forms
- Simple newsletter signup
- Basic admin inputs
- Forms with minimal validation
Example async validator scenario:
- User types username
- Form waits briefly using debounce
- API checks availability
switchMapcancels stale checks- Validation result updates the control
Interviewers may also ask about:
- Custom validators
- Cross-field validators
- Async validators
ControlValueAccessor- Typed forms
- Form arrays
A strong answer is:
“I usually choose reactive forms for complex or highly validated application forms because the model is explicit, typed, and testable. Template-driven forms remain useful for simple forms with limited logic.”
What are Signal Forms and when would you choose them over reactive forms?
What interviewers are testing: Whether you know the newer signal-native forms model but can still make a pragmatic migration decision instead of declaring reactive forms obsolete.
Signal Forms are stable in Angular 22 and model form state with signals instead of the traditional FormControl/FormGroup observable model.
Signal Forms wrap a writable signal model in a field tree. The underlying model remains the source of truth, while field APIs expose validation and interaction state such as value, errors, touched, and validity.
| Approach | When to choose |
|---|---|
| Reactive forms | Complex validation, dynamic fields, enterprise apps, existing codebases |
| Template-driven | Simple forms with minimal logic |
| Signal Forms | New signal-first components where form state integrates tightly with signals |
Signal Forms do not replace reactive forms overnight—large existing applications will use reactive forms for years.
A strong answer is:
“Signal Forms fit new signal-first UIs because the form model and validation state integrate naturally with signals. I would still keep reactive forms in established applications where their APIs, validators, and surrounding architecture are already working well.”
Lazy loading and route configuration in standalone apps?
What interviewers are testing: Whether you can design route boundaries that improve initial load time while choosing the correct guard, resolver, and preloading behavior.
Lazy loading reduces the initial JavaScript bundle by loading route code only when needed.
In standalone Angular apps, routes are usually configured as plain route arrays.
export const routes: Routes = [
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
},
{
path: 'item/:id',
loadComponent: () =>
import('./item/item.component').then(m => m.ItemComponent),
},
];Use:
| API | Best for |
|---|---|
loadComponent |
Lazy loading one standalone component |
loadChildren |
Lazy loading a feature route tree |
canMatch |
Prevent matching/loading routes based on conditions |
canActivate |
Protect route activation |
resolve |
Fetch data before route activation |
Modern Angular also supports functional guards and resolvers:
export const authGuard: CanActivateFn = () => {
return inject(AuthStore).isLoggedIn();
};Important senior discussion:
- Use
canMatchwhen you want to stop a route from matching or loading - Use
canActivatewhen the route can match but needs access control - Guards are navigation controls, not an authorization boundary; APIs must enforce authorization server-side
- Use lazy loading for large feature areas, admin panels, reports, and rarely used routes
- Use preloading strategies when you want faster navigation after initial load
- Avoid putting all routes into one large eager bundle
Resolvers are still useful, but for many modern apps, component-level loading with signals/resources gives more flexible loading and error states.
A strong answer is:
“In standalone Angular, I lazy load a single component with
loadComponentor a feature route tree withloadChildren. I use guards to control client-side navigation, resolvers when data must exist before activation, and preloading when faster subsequent navigation justifies it. Backend APIs still enforce authorization.”
What do HTTP interceptors do?
What interviewers are testing: Whether you know interceptors wrap the HttpClient pipeline for auth headers, logging, and error handling—not business logic in every service.
HTTP interceptors sit in the HttpClient pipeline. They can inspect or transform outgoing requests and incoming responses.
Common uses:
- Add auth tokens
- Add correlation/request IDs
- Normalize base URLs
- Log request timing
- Map API errors
- Handle 401/403 responses
- Retry selected requests
Modern standalone-friendly style uses functional interceptors:
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(AuthStore).token();
const authReq = token
? req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })
: req;
return next(authReq);
};Registration example:
provideHttpClient(
withInterceptors([authInterceptor])
);Keep interceptors thin. Heavy business logic should stay in services.
Good interceptor logic:
- Add token
- Attach headers
- Log timing
- Coordinate global authentication failure handling
- Convert technical errors to application errors
Bad interceptor logic:
- Component-specific UI decisions
- Complex business workflows
- Silent retry of unsafe writes
- Large conditional logic for many unrelated APIs
A strong answer is:
“An interceptor wraps the HTTP pipeline. I use it for cross-cutting concerns such as auth headers, logging, correlation IDs, and global error handling, while keeping feature-specific logic inside services.”
Dependency injection and architecture
How does Angular dependency injection work?
What interviewers are testing: Whether you understand Angular's hierarchical injector model and can choose service lifetime/scope deliberately rather than treating every service as a root singleton.
Angular dependency injection lets classes ask for dependencies instead of creating them manually.
Example service:
@Injectable({ providedIn: 'root' })
export class UserService {}Example injection:
private readonly userService = inject(UserService);Angular resolves a token from an injector hierarchy. Common provider scopes:
| Provider location | Result |
|---|---|
providedIn: 'root' |
App-wide singleton |
| Application config providers | App-level provider |
| Route providers | Provider scoped to route tree |
Component providers |
New instance for that component subtree |
inject() is commonly used in:
- Components
- Services
- Functional guards
- Functional resolvers
- Functional interceptors
- Factory providers
Constructor injection is still valid:
constructor(private userService: UserService) {}Senior-level point: component-level providers can intentionally create isolated instances.
Example:
@Component({
providers: [WizardStateService],
})
export class CheckoutWizardComponent {}Each wizard subtree gets its own state service instead of sharing one global singleton.
A strong answer is:
“Angular DI resolves tokens through a hierarchy of injectors.
providedIn: 'root'gives an app-wide singleton, while route or component providers create narrower scopes. I choose provider scope based on the lifetime of the state or service.”
@Host, @Self, @SkipSelf, @Optional?
What interviewers are testing: Whether you understand how Angular walks the injector hierarchy and can deliberately constrain that lookup in reusable directives/components.
DI modifiers control how Angular searches for a dependency in the injector hierarchy.
| Modifier | Meaning |
|---|---|
@Self() / { self: true } |
Look only in the current injector |
@SkipSelf() / { skipSelf: true } |
Skip current injector and start from parent |
@Host() / { host: true } |
Stop searching at the host boundary |
@Optional() / { optional: true } |
Return null instead of throwing if not found |
Modern inject() style:
const parentConfig = inject(ConfigService, {
skipSelf: true,
optional: true,
});When they are useful:
Self— a directive requires a local provider on the same elementSkipSelf— a child wants the parent instance, not its ownOptional— a reusable component can work with or without a providerHost— a library component should not accidentally resolve too far up the tree
Example scenario:
A reusable form-control component may optionally read a parent form field provider. If the provider is missing, the component should still render instead of crashing.
const parent = inject(ControlContainer, { optional: true });These modifiers are more common in framework/library code than everyday feature components.
A strong answer is:
“DI modifiers restrict Angular’s provider lookup. I use them when building reusable components, directives, or libraries where provider scope must be explicit and accidental parent resolution would create bugs.”
When would you choose NgRx vs signals in services?
What interviewers are testing: Whether you can recognize when simple signal state is enough and when larger shared state needs stronger conventions, traceability, effects, or team-wide architecture.
Use the simplest state management that fits the problem.
| Choose signals + service | Choose NgRx / SignalStore |
|---|---|
| Feature-local UI state | Large shared state |
| Simple derived values | Complex state transitions |
| Small team or small feature | Multiple teams touching same state |
| No strict event history needed | Actions/events improve traceability |
| Fast iteration | Enterprise conventions and DevTools matter |
Signals in a service work well for:
- Filters
- Selected tab
- Modal state
- Local feature preferences
- Current loaded entity
- Simple derived view models
NgRx is useful when you need:
- Clear action history
- Reducer/effect discipline
- DevTools
- Complex async workflows
- Shared state across many routes
- Large-team consistency
SignalStore is a pragmatic middle ground. It gives a structured store pattern while using Angular signals naturally.
Avoid introducing NgRx solely because it is common in Angular projects. A three-screen internal tool may not need actions, reducers, effects, and selectors everywhere.
But also avoid under-engineering. If many components mutate shared state in different ways, a formal store can prevent bugs.
A strong answer is:
“I start with signals in a service for simple feature state. I move to NgRx or SignalStore when state becomes shared, complex, event-driven, or maintained by multiple teams. I do not use NgRx just because the project is Angular.”
Smart vs presentational components?
What interviewers are testing: Whether you can separate orchestration/data access from reusable UI behavior without turning the pattern into unnecessary abstraction.
Smart and presentational components separate data orchestration from reusable UI rendering.
Presentational components:
- Receive data through inputs
- Emit events through outputs
- Do not call HTTP APIs directly
- Do not know about global routing/store details
- Are easy to reuse and test
user = input.required<User>();
selected = output<string>();Smart/container components:
- Load data
- Inject services or stores
- Handle route params
- Build view models
- Pass data down to presentational components
- React to output events
Typical flow:
- Container reads route params
- Container loads data through service/store
- Container passes data to UI component
- UI component emits user action
- Container calls service/store
This pattern prevents reusable UI components from becoming tied to one backend or route.
Folder example:
user/
containers/
user-page.component.ts
ui/
user-card.component.ts
user-table.component.ts
data-access/
user-api.service.tsDo not take the pattern too far. Tiny features do not need unnecessary folders and indirection.
A strong answer is:
“Presentational components render data and emit events. Smart components fetch data, coordinate services, and handle state. This keeps UI components reusable and prevents API logic from spreading across the template layer.”
Nx / monorepo boundaries for Angular?
What interviewers are testing: In large-team roles, whether you can enforce architectural ownership and dependency direction across many applications/libraries—not whether you remember Nx commands.
Nx helps manage large Angular workspaces with multiple apps and libraries.
Common monorepo structure:
apps/
admin/
customer-portal/
libs/
shared/ui/
shared/util/
users/feature/
users/data-access/
users/ui/Important concepts:
| Concept | Meaning |
|---|---|
| App | Deployable application |
| Library | Reusable code unit |
| Feature lib | Route/page-level feature logic |
| UI lib | Reusable presentational components |
| Data-access lib | API clients, stores, facades |
| Util lib | Pure helpers and shared types |
| Boundary rules | Prevent invalid imports |
| Affected builds | Test/build only what changed |
Why boundaries matter:
- Prevent circular dependency problems
- Stop UI libraries from importing data-access internals
- Keep features independent
- Make refactoring safer
- Allow teams to own separate areas
- Speed up CI with affected commands
Example boundary idea:
| Library type | Can depend on |
|---|---|
feature |
ui, data-access, util |
ui |
util only |
data-access |
util only |
util |
No feature-specific library |
Senior interviewers may ask about:
@nx/enforce-module-boundaries- Tag-based dependency constraints
- Circular dependencies
- Shared UI libraries
- Publishable vs buildable libraries
- Affected builds and parallel CI
- Ownership between teams
A good answer should focus on architecture, not only tooling.
A strong answer is:
“Nx is useful when an Angular codebase grows into many apps and libraries. I use libraries to separate feature, UI, data-access, and utility code, then enforce boundaries so teams do not create circular dependencies or import across layers incorrectly.”
Performance, testing, and security
How do you optimize Angular app performance?
What interviewers are testing: Whether you measure before optimizing, identify whether the bottleneck is rendering, change detection, JavaScript, network, or backend work, and verify the improvement afterward.
A good performance answer should start with measurement, not random optimization.
First measure with:
- Angular DevTools
- Chrome Performance panel
- Lighthouse / Core Web Vitals
- Network tab and bundle analysis
- Real user monitoring if available
Then optimize based on the bottleneck.
| Problem | Useful technique |
|---|---|
| Too much change-detection work | Audit Eager/legacy components, signal dependencies, expensive bindings, and component boundaries |
| Large lists | @for with track, CDK virtual scroll |
| Heavy initial bundle | Lazy routes, @defer, code splitting |
| Slow images | NgOptimizedImage, width/height, priority hints |
| Expensive template work | Avoid template functions, precompute with computed() |
| Repeated HTTP calls | Cache carefully, share streams, use state services |
| Slow public page load | SSR/hydration or SSG where useful |
| Unused services/code | Tree-shakable providers and dead-code removal |
Modern Angular performance tools include:
- OnPush-by-default change detection and auditing legacy Eager components
- Signals and
computed() - Built-in control flow with
@for @deferfor heavy widgets- Lazy route loading
- SSR/hydration for faster first render
- Virtual scrolling for large lists
Example list rendering:
<!-- Avoid for reorderable data -->
@for (user of users(); track $index) {
<app-user-card [user]="user" />
}
<!-- Prefer stable identity -->
@for (user of users(); track user.id) {
<app-user-card [user]="user" />
}Because @for requires track, choose a stable unique identity such as user.id; using $index for a reorderable list can cause poor DOM reuse.
Senior-level point: performance is not only frontend code. Sometimes the real fix is backend pagination, smaller API payloads, CDN caching, or image optimization.
A strong answer is:
“I first measure the bottleneck, then optimize the right layer. In Angular, I usually look at change detection, list rendering, bundle size, lazy loading, image loading, and duplicate async work before making changes.”
SSR and hydration — interview basics?
What interviewers are testing: Whether you understand the difference between rendering HTML on the server and hydrating it in the browser, and can decide when SSR's complexity is justified.
SSR means Angular renders the initial HTML on the server. The browser receives meaningful HTML before the full client-side JavaScript finishes loading.
Hydration means Angular reuses the server-rendered DOM and attaches client-side behavior to it instead of destroying and recreating the page.
| Concept | Meaning |
|---|---|
| CSR | Browser renders the app after JavaScript loads |
| SSR | Server sends already-rendered HTML |
| SSG | HTML generated at build time |
| Hydration | Client Angular attaches behavior to server HTML |
| Incremental hydration | Hydrate deferred parts of the page only when needed |
SSR/hydration can help with:
- Faster first content
- Better perceived performance
- SEO for public pages
- Lower client-side rendering cost at startup
But SSR is not always required. For an internal authenticated dashboard, SSR may be less valuable than lazy loading, caching, and API performance.
Common hydration pitfalls:
- Direct DOM access too early
- Using
window,document, orlocalStorageduring server render - Random IDs or dates causing server/client mismatch
- Third-party scripts modifying DOM before hydration
- Different templates rendered on server and client
Safer patterns:
- Use
afterNextRender()for DOM-dependent browser work - Guard browser-only APIs when SSR is enabled
- Keep templates deterministic
- Defer heavy client-only widgets
A strong answer is:
“SSR gives the browser HTML earlier, and hydration attaches Angular behavior without a full client re-render. I use it mainly for public or performance-sensitive pages, but I watch for DOM mismatch and browser-only API issues.”
What testing strategy do interviewers expect?
What interviewers are testing: Whether you can place tests at the cheapest reliable layer and avoid both brittle implementation tests and an oversized E2E suite.
Interviewers expect a practical testing strategy, not just “we write unit tests.”
A healthy Angular testing approach includes:
| Test type | What to test |
|---|---|
| Unit tests | Services, validators, pipes, reducers, pure functions |
| Component tests | Inputs, outputs, template behavior |
| HTTP tests | API service behavior without real network calls |
| Integration tests | Feature flow with multiple components/services |
| E2E tests | Critical user journeys in browser |
Modern Angular HTTP tests should use provideHttpClientTesting().
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
provideHttpClient(),
provideHttpClientTesting(),
UserApiService,
],
});
});Component input test example:
it('shows users', () => {
const fixture = TestBed.createComponent(UserListComponent);
fixture.componentRef.setInput('users', [
{ id: '1', name: 'Ada' },
]);
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('Ada');
});Good testing habits:
- Test behavior, not implementation details
- Mock API services at clear boundaries
- Avoid brittle DOM selectors
- Test custom validators separately
- Use Angular Material/CDK harnesses where available
- Keep E2E tests focused on important flows
Senior-level answer: do not test every small DOM detail with E2E. Use many fast unit/component tests and fewer high-value browser tests.
A strong answer is:
“I use unit tests for logic, component tests for UI behavior, HTTP tests for API services, and a small number of E2E tests for critical journeys. I avoid brittle tests that only verify implementation details.”
Angular security topics for interviews?
What interviewers are testing: Whether you know what Angular protects automatically, where developers can bypass those protections, and which security guarantees must still be enforced by the backend.
Angular has built-in security protections, but developers still need to follow safe patterns.
Important Angular security topics:
| Topic | What to say |
|---|---|
| XSS | Avoid rendering untrusted HTML |
| Sanitization | Angular sanitizes many bindings automatically |
DomSanitizer |
Use bypass APIs only when content is truly trusted |
| CSRF/XSRF | Cookie-based auth needs server/client token support |
| Auth/session storage | Choose based on threat model; browser-readable bearer tokens increase XSS exposure, while cookie-based sessions require secure cookie and CSRF controls |
| Route guards | UX/access control, not backend security |
| Interceptors | Add auth headers and handle auth errors |
| CSP / Trusted Types | Useful defense-in-depth for strict security apps |
Example risk:
<div [innerHTML]="userProvidedHtml"></div>Angular sanitizes HTML bindings, but you should still avoid displaying untrusted rich HTML unless the product really requires it.
Dangerous pattern:
this.sanitizer.bypassSecurityTrustHtml(userInput);That tells Angular to trust the value. Do this only for content from a trusted source, never blindly for user input.
Route guards are not enough security. A user can still call backend APIs directly. The backend must enforce authorization.
Good interview answer should mention both frontend and backend:
- Frontend protects UI and reduces attack surface
- Backend enforces real authorization
- Security-sensitive decisions must not depend only on Angular route guards
A strong answer is:
“Angular helps prevent XSS through sanitization, but I still avoid untrusted HTML, use
DomSanitizervery carefully, handle CSRF/auth correctly, and never treat route guards as the only security layer.”
Scenario-based and migration questions
Scenario: Implement search typeahead with HTTP.
What interviewers are testing: Whether you can turn UI requirements into a correct RxJS pipeline and explain why switchMap prevents stale-result races.
A strong typeahead design avoids sending a request for every keystroke and avoids showing stale results.
Typical flow:
- Create a
FormControl - Listen to
valueChanges - Add
debounceTime(300) - Add
distinctUntilChanged() - Ignore very short search terms if needed
- Use
switchMap()to call HTTP - Handle loading and errors
- Render with
asyncpipe or convert to a signal
Example:
results$ = this.searchControl.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
filter(term => term.length >= 2),
switchMap(term =>
this.api.search(term).pipe(
catchError(() => of([]))
)
)
);Why switchMap()?
Because the latest search should win. If the user types a, then an, then ang, older requests should not update the screen after the latest request.
Do not use mergeMap() for normal typeahead. It allows parallel requests, so a slower old request may return later and show stale results.
Good UI details:
- Show loading state
- Show empty state
- Show error state
- Disable search for very short terms
- Cache results if the same query repeats often
- Use OnPush and avoid template functions in result lists
A strong answer is:
“For typeahead, I debounce input, ignore duplicate terms, and use
switchMapso the previous search subscription is cancelled when a newer term arrives. With HttpClient, that also aborts the obsolete request, preventing stale results.”
Scenario: Architect a large analytics dashboard?
What interviewers are testing: Whether you can break a large UI into routing, state, data-access, rendering, and resilience boundaries—and defend where you would use signals, a store, lazy loading, and backend optimization.
For a large analytics dashboard, split the design into routing, state, rendering, API, and resilience.
Key architecture choices:
| Area | Approach |
|---|---|
| Routing | Lazy load dashboard sections |
| Shared filters | Signal service, SignalStore, or NgRx |
| Widgets | Presentational chart/table components |
| Data access | Dedicated API/data-access services |
| Rendering | Signals, stable component boundaries, @defer |
| Tables | Pagination or CDK virtual scroll |
| Error handling | Per-widget error and retry state |
| Security | Guard routes and enforce backend authorization |
A good dashboard should not fail as one giant page. Each widget should be able to show:
- Loading state
- Empty state
- Error state
- Retry action
- Last updated timestamp where useful
Performance plan:
- Lazy load heavy routes
- Defer heavy charts below the fold
- Use API pagination for large tables
- Use
@forwith stabletrack - Avoid recalculating chart data in templates
- Cache shared reference data
- Reduce repeated HTTP calls between widgets
State management decision:
- Simple dashboard: signal service or SignalStore
- Many widgets and teams: NgRx/SignalStore with clearer conventions
- Complex filter sync with URL: route query params + store/facade
Senior-level angle: frontend optimization may not be enough. For reporting dashboards, backend aggregation, API shape, caching, and database queries often matter more than Angular code.
A strong answer is:
“I would lazy load dashboard sections, keep shared filters in a clear store/facade, make widgets presentational, keep their state and rendering predictable, defer heavy widgets with
@defer, and give each widget independent loading, error, and retry states.”
Scenario: Migrate NgModule app to standalone?
What interviewers are testing: Whether you can reduce migration risk through sequencing, compatibility, testing, and rollback—not merely convert syntax from NgModules to standalone components.
A good migration answer should be incremental, not a big-bang rewrite.
Suggested plan:
- Confirm the current Angular version and decide whether a framework upgrade should be completed before the standalone migration
- Run tests and fix version-related breaking changes first
- Convert leaf components to standalone
- Add required
importsdirectly in components - Convert route-level components and lazy routes
- Replace module-based routing with route arrays
- Move toward
bootstrapApplication() - Remove empty or unnecessary NgModules
- Keep NgModule interop where migration risk is high
- Measure bundle size and test each feature slice
Migration schematic example:
ng generate @angular/core:standaloneAngular's schematic uses three broad phases—convert declarations, remove unnecessary modules, then switch bootstrap—while a real application can roll those phases through feature slices incrementally.
Good migration order:
leaf UI components
→ feature components
→ route configuration
→ bootstrap
→ shared module cleanupAvoid these mistakes:
- Converting the whole application in one PR
- Removing shared modules before imports are clear
- Mixing version upgrades and architecture migration without tests
- Rewriting working features only for style
- Ignoring lazy route boundaries
Signals migration can happen separately. Do not force signal inputs into every component during the first standalone migration unless that component is already being changed.
A strong answer is:
“I migrate NgModule apps to standalone gradually. I start with leaf components, then feature routes, then bootstrap, while keeping tests green and avoiding a risky big-bang rewrite.”
Scenario: Migrate BehaviorSubject UI state to signals?
What interviewers are testing: Whether you first classify the state as current state versus asynchronous stream, rather than mechanically replacing every BehaviorSubject with a signal.
A good migration starts by identifying what kind of state the BehaviorSubject represents.
Keep RxJS when the state is really a stream:
- Websocket events
- Debounced user input
- Retry/cancel HTTP pipelines
- Complex async composition
- Multiple event sources needing operators
Use signals when the state is current UI/application state:
- Selected tab
- Current filters
- Expanded rows
- Loaded user shown in template
- Derived view model
Before:
private readonly filterSubject = new BehaviorSubject('');
readonly filter$ = this.filterSubject.asObservable();
setFilter(value: string) {
this.filterSubject.next(value);
}After:
private readonly filter = signal('');
readonly currentFilter = this.filter.asReadonly();
setFilter(value: string) {
this.filter.set(value);
}Derived state:
readonly filteredUsers = computed(() =>
this.users().filter(user =>
user.name.includes(this.currentFilter())
)
);Migration steps:
- Find
BehaviorSubjectstate used mainly by templates - Replace with private writable signal
- Expose read-only signal
- Move derived values to
computed() - Keep HTTP as observable when RxJS operators are useful
- Use
toSignal()at the component boundary if needed - Remove manual subscriptions from components
- Add tests for old and new behavior
Do not migrate blindly. A BehaviorSubject used inside a complex RxJS pipeline may be better left as RxJS.
A strong answer is:
“I migrate BehaviorSubject state to signals when it represents current UI state. I keep RxJS for async streams and operator-heavy workflows, then bridge to signals near the template.”
AngularJS (1.x) vs modern Angular — still asked?
What interviewers are testing: Whether you can distinguish AngularJS 1.x from modern Angular and discuss incremental migration without pretending the two frameworks are versions of the same architecture.
Yes, it is still asked in some legacy enterprise interviews.
AngularJS and modern Angular are different frameworks.
| AngularJS 1.x | Modern Angular |
|---|---|
| JavaScript-first | TypeScript-first |
Controllers and $scope |
Components and services |
| Digest cycle | Change detection with zones/signals |
ng-repeat |
*ngFor / @for |
ng-if |
*ngIf / @if |
| Older architecture | Standalone components, signals, RxJS |
In interviews, clarify what the job description means:
- AngularJS usually means version 1.x legacy apps
- Angular usually means Angular 2+ modern TypeScript framework
If the company still has AngularJS, they may ask about migration. Good migration talking points:
- Do not rewrite everything at once
- Identify stable feature boundaries
- Upgrade build/test pipeline first
- Migrate route by route or feature by feature
- Keep old and new systems working during transition
- Replace
$scopepatterns with component/service architecture
A strong answer is:
“AngularJS is the older 1.x framework based on controllers and
$scope. Modern Angular is a TypeScript component framework. If I see both in a company, I clarify whether the role is maintenance, migration, or new Angular development.”
How do you approach accessibility in Angular apps?
What interviewers are testing: Whether accessibility is part of your component-design and testing process—not something you solve later by adding ARIA attributes.
Accessibility should be part of normal Angular development, not a final polish step.
Important practices:
- Use semantic HTML first
- Use real buttons and links where possible
- Add labels for form controls
- Ensure keyboard navigation works
- Manage focus in dialogs and route changes
- Use ARIA only when native HTML is not enough
- Provide visible focus styles
- Maintain color contrast
- Announce important dynamic updates
- Test with keyboard, screen reader smoke tests, axe, and Lighthouse
Angular/CDK tools:
| Tool | Use |
|---|---|
| CDK a11y | Accessibility helpers |
| Focus trap | Keep focus inside dialogs/modals |
| Live announcer | Announce dynamic updates to screen readers |
| List/key managers | Keyboard navigation for custom widgets |
Common Angular-specific examples:
- After opening a modal, move focus into it
- After closing a modal, return focus to the opener
- For custom dropdowns, support arrow keys and Escape
- For async validation errors, make the message readable to screen readers
- For route changes, update page title and focus the main content
Avoid building custom components when native controls work. A native <button> is usually better than a clickable <div>.
A strong answer is:
“I start with semantic HTML, then add keyboard support, focus management, labels, contrast, and screen-reader announcements where needed. For complex widgets, I use Angular CDK a11y utilities instead of inventing everything manually.”
What changed with built-in control flow (@if, @for)?
What interviewers are testing: Whether you understand both the syntax change and its runtime implications—especially item identity and DOM reuse in @for.
Modern Angular supports built-in template control flow.
Older style:
<div *ngIf="user">
{{ user.name }}
</div>
<div *ngFor="let item of items; trackBy: trackById">
{{ item.name }}
</div>Modern style:
@if (user(); as u) {
<p>{{ u.name }}</p>
} @else {
<p>Guest</p>
}
@for (item of items(); track item.id) {
<app-row [item]="item" />
} @empty {
<p>No items found</p>
}Benefits:
- Clearer syntax
- Better type narrowing
- Less reliance on structural directive imports
- More explicit tracking in loops
- Better fit with signals
@for requires thinking about identity. Use a stable tracking expression:
@for (user of users(); track user.id) {
<app-user-card [user]="user" />
}Avoid tracking by index when the list can be reordered, inserted into, or deleted from. Tracking by stable ID prevents unnecessary DOM churn and identity bugs.
@defer is related but different. It delays loading/rendering a block until a trigger happens.
@defer (on viewport) {
<app-heavy-chart />
} @placeholder {
<p>Chart loading...</p>
}A strong answer is:
“Built-in control flow gives Angular templates clearer
@if,@for, and@switchsyntax. I especially pay attention to stabletrackvalues in@forbecause they directly affect DOM reuse and performance.”
What TypeScript depth do senior Angular interviews expect?
What interviewers are testing: Whether TypeScript actually shapes your Angular design—API models, state machines, forms, nullability and unsafe boundaries—or whether you only use it for basic annotations.
Senior Angular interviews expect more than basic TypeScript syntax.
Important TypeScript areas:
| Topic | Angular use |
|---|---|
| Generics | Observable<T>, typed services, typed forms |
| Union types | Loading/error/success UI states |
| Discriminated unions | Safer state machines |
readonly |
Immutable inputs and state |
unknown vs any |
Safer API/error handling |
| Utility types | Partial<T>, Pick<T>, Record<K,V> |
satisfies |
Validate object shape without losing inference |
| Strict null checks | Safer templates and API models |
| Type guards | Narrow unknown API values |
| Typed forms | Safer reactive form controls |
Example UI state model:
type UserState =
| { status: 'loading' }
| { status: 'loaded'; users: User[] }
| { status: 'error'; message: string };This is better than several unrelated booleans:
isLoading = false;
hasError = false;
users = [];
errorMessage = '';Because impossible states become harder to represent.
Senior candidates should also understand API typing:
- DTO from backend may differ from UI model
- Nullable fields should be modeled honestly
- Avoid
anyfor HTTP responses - Use
unknownat unsafe boundaries and validate/narrow it
For deeper practice on generics, narrowing, discriminated unions, unknown, and utility types, see TypeScript interview questions.
A strong answer is:
“Senior Angular developers should use TypeScript to model real application states, not just annotate variables. I use generics, strict null checks, discriminated unions, and typed forms to catch bugs before runtime.”
Behavioral: tell me about a difficult Angular migration or performance fix.
What interviewers are testing: Whether you can connect technical decisions to measurable production outcomes while showing ownership, risk management and collaboration.
Use the STAR format.
| Part | What to cover |
|---|---|
| Situation | What was broken or risky? |
| Task | What were you responsible for? |
| Action | What did you change? |
| Result | What improved measurably? |
Example structure:
Situation:
The dashboard was slow because a large table and several charts rendered at once.
Task:
Improve load time without rewriting the full application.
Action:
- Profiled with Angular DevTools and Chrome Performance
- Converted legacy Eager components to OnPush where profiling showed unnecessary checking
- Replaced template functions with computed values/pipes
- Added stable
trackvalues to list rendering - Deferred heavy charts below the fold
- Moved repeated API calls into a shared data service
- Worked with backend team on pagination
Result:
- Initial render time improved by the measured amount
- Duplicate API requests were eliminated or reduced
- Table interaction latency improved in profiling
- The team adopted a checklist for future dashboard widgets
In a real interview, replace these with your actual numbers—for example load time, bundle size, request count, or interaction latency.
For migration stories, mention:
- Incremental rollout
- Tests before each slice
- Feature flags if needed
- Rollback plan
- Bundle/performance comparison
- Developer communication
Avoid vague answers like “I optimized the app.” Interviewers want to hear what you measured, what you changed, and what result you achieved.
A strong answer is:
“I would explain the production problem, how I measured it, the Angular-specific fixes I applied, and the measurable result. A strong story shows technical skill, risk control, and collaboration.”
What is the resource() / httpResource() API (modern async)?
What interviewers are testing: Whether you understand Angular's newer signal-oriented async model and can explain where it simplifies read-oriented UI without claiming that it replaces RxJS.
Angular’s resource APIs model async data as reactive state.
httpResource() is a signal-friendly wrapper around HttpClient. It gives you request state such as value, loading, and error in a way that works naturally with signals and templates.
Typical idea:
userResource = httpResource<User>(() =>
`/api/users/${this.userId()}`
);Template usage:
@if (userResource.isLoading()) {
<p>Loading...</p>
} @else if (userResource.error()) {
<p>Unable to load user</p>
} @else {
<app-user-card [user]="userResource.value()" />
}Why it matters:
- Reduces manual
subscribe()code - Reduces manual loading/error flags
- Reacts to signal changes
- Works well with
computed()and templates - Still uses Angular
HttpClientunderneath
Good use cases:
- Loading data from route params
- Search/detail pages
- Read-heavy screens
- Template-friendly async state
Where RxJS may still be better:
- Complex operator pipelines
- Websocket streams
- Debounced typeahead
- Multi-step workflows
- Writes with careful retry/cancel rules
- Existing service APIs already built around observables
Interview-safe answer: know the API exists, but do not claim it replaces all RxJS.
A strong answer is:
“
resource()andhttpResource()help model async data as signal-based state with loading, value, and error handling. I would use them for read-heavy UI data, while keeping RxJS for complex streams and operator-heavy workflows.”
Final-week Angular interview checklist
Use the final week to revise common interview patterns, not every Angular API.
Must revise:
- Component basics, standalone imports, and smart/dumb split
-
input(),output(),model(), and legacy@Input/@Output - Lifecycle hooks,
afterNextRender(), and cleanup - OnPush, signals, and change detection
- Zone.js vs zoneless Angular
-
signal(),computed(), andeffect() - Signals vs RxJS
-
switchMap,mergeMap,concatMap,exhaustMap -
asyncpipe andtakeUntilDestroyed() - Reactive forms, validators, and
ControlValueAccessor - Lazy routes, guards, and resolvers
- HTTP interceptors and error handling
- DI scopes and modifiers
- NgRx vs signal services vs SignalStore
- Performance: OnPush,
@for track,@defer, lazy loading - Testing strategy and HTTP testing
- Security: XSS, sanitization, CSRF, route guards
- Accessibility basics
- Migration from NgModule to standalone
- Migration from BehaviorSubject state to signals
Scenario drills:
- Build a search typeahead
- Fix stale search results
- Prevent double form submit
- Design a large dashboard
- Debug a memory leak
- Improve a slow list/table
- Add auth token with interceptor
- Create a custom form control
- Migrate one feature to standalone
- Explain a production performance fix using STAR
Good closing line for an interview:
“I choose Angular patterns based on the problem. I use standalone components and signals for clear component architecture and UI state, RxJS where stream composition matters, and formal stores when shared state complexity justifies them. I can work with modern Angular while also maintaining and migrating older Angular applications safely.”
Pattern cheat sheet (quick reference)
| Need | Reach for |
|---|---|
| Typeahead HTTP | debounceTime + distinctUntilChanged + switchMap |
| Prevent double submit | exhaustMap |
| Ordered writes | concatMap |
| Parallel independent calls | mergeMap |
| UI state | signal() + computed() |
| Complex async streams | RxJS operators |
| Template observable | async pipe |
| Manual component subscription | takeUntilDestroyed() |
| Non-trivial forms | Reactive forms |
| Custom form component | ControlValueAccessor |
| Lazy page | loadComponent or loadChildren |
| Auth header | HttpInterceptorFn |
| Large list | @for with stable track, virtual scroll |
| Heavy widget | @defer |
| Public SEO page | SSR/hydration or SSG |
| Feature-local state | Signal service |
| Enterprise shared state | SignalStore or NgRx |
| Library boundaries | Nx tags and module-boundary rules |
| Accessibility widgets | Angular CDK a11y |
| Security-sensitive HTML | Avoid or sanitize carefully |
References
Official Angular documentation
On-site interview prep
- TypeScript interview questions — generics, narrowing,
unknownvsany, utility types, and typed application state - Front end developer interview questions — browser fundamentals, accessibility, Core Web Vitals, and UI architecture
- CSS interview questions — HTML semantics, layout, responsive design, and CSS debugging
- Full stack developer interview questions — APIs, authentication, HTTP, databases, and end-to-end system design
Summary
Angular interviews—especially at the senior level—focus on signals, RxJS discipline, standalone architecture, and change detection more than CLI trivia. Practice defending operator choices, migrating legacy NgModule apps, and keeping templates fast with OnPush, track, and @defer. Use this guide as a self-test: answer aloud, then compare your structure to each collapsed response. Pair with front end and full stack prep when the role spans the whole delivery stack.

