Junior Angular interviews ask "what is a component." Senior interviews ask why your dashboard re-runs change detection thousands of times a second, why a typeahead fires a request per keystroke and shows stale results, and why that subscription leaks memory until the tab crawls. Angular is a big, opinionated framework, and seniority is knowing the machinery underneath — change detection, RxJS, the new signals model, and where performance goes to die.
This is 30 questions across four blocks. Each answer is a short answer you could say in one breath, a deep dive into the mechanism (with code), and the senior signal the interviewer is listening for. Examples use modern Angular (v17+: standalone, signals, the new control flow) and are grounded in Mattrx — a real multi-tenant marketing-analytics platform whose internal admin console is built in Angular.
The lens for all 30
A junior uses the APIs; a senior understands change detection, the reactivity model, and RxJS semantics — and designs to work with them. "Why is the app janky?" gets a shrug from a junior and "default CD is re-checking a 2,000-node tree on every mousemove — OnPush + signals cut it to the subtree that changed" from a senior.
Block B highlight — how change detection actually works
Angular runs CD to sync model → DOM. Zone.js monkey-patches async APIs (events, timers, XHR) so after any async task, Angular walks the component tree top-down and dirty-checks each binding.
async event (click / http / timer)
-> zone.js notifies Angular
-> CD walks the component tree top-down, checks each binding
-> updates the DOM where a bound value changed
Default vs OnPush: Default checks every component every run. OnPush skips a component unless an @Input reference changed, an event fired inside it, or an async-bound observable emitted. On big trees OnPush is the single biggest perf lever.
@Component({
selector: 'campaign-list',
changeDetection: ChangeDetectionStrategy.OnPush,
template: \`@for (c of campaigns(); track c.id) { <campaign-row [campaign]="c" /> }\`,
})
export class CampaignListComponent {
campaigns = input.required<Campaign[]>(); // new reference -> this subtree is checked
}
Senior signal: you default to OnPush, know it relies on immutable inputs, and can name the three triggers that still run it.
Block C highlight — the RxJS flattening operators
The high-stakes RxJS question. They all map to an inner observable and flatten, differing in how they handle overlap:
switchMap: new value -> CANCEL previous, start new (search/typeahead)
concatMap: queue -> run one after another, in order (ordered writes)
mergeMap: run all concurrently (independent parallel)
exhaustMap: ignore new while one runs (dedupe rapid submits)
// Typeahead: switchMap cancels the in-flight request when a new term arrives -> no stale results.
this.search.valueChanges.pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.api.search(term)),
).subscribe(r => this.results.set(r));
Pick switchMap for search, exhaustMap for submit, concatMap for ordered writes, mergeMap for independent parallel work. Most "senior" candidates get tripped here.
Block C highlight — signals vs RxJS
Signals are Angular's synchronous reactive primitive — a value you read as a function, with computed() for derived values and effect() for side effects. They power fine-grained, zoneless CD. RxJS stays for async streams/events; signals are for synchronous state. Bridge with toSignal / toObservable.
count = signal(0);
double = computed(() => this.count() * 2); // recomputes only when count changes
constructor() { effect(() => console.log(this.count())); }
this.count.update(n => n + 1); // triggers dependents precisely
And preventing leaks — prefer the async pipe or takeUntilDestroyed() over manual ngOnDestroy bookkeeping:
this.api.pollKpis().pipe(takeUntilDestroyed()).subscribe(k => this.kpis.set(k));
The through-line
Angular is fast and maintainable when you cooperate with change detection (OnPush, immutability, signals), pick the right RxJS operator by its overlap semantics, and tear down subscriptions (async pipe / takeUntilDestroyed). Fight it and you get jank and leaks. Answer each question with the mechanism and the failure it prevents, and you sound like someone who has shipped and profiled a real Angular app.
The full guide has all 30 — components & DI, change detection & performance, RxJS & state, and routing/forms/testing/production — each with the short answer, the deep dive with code, and the exact senior signal:
Originally published on PrepStack.
Top comments (0)