This comprehensive guide covers Angular interview questions from basic to advanced levels, helping you prepare for technical interviews at any stage of your career.How to use this guide: Do not memorize answers — interviewers can spot rehearsed responses immediately. Instead, understand the why behind each answer so you can reason through variations. A senior engineer would say: “I know the trade-offs and can explain when I would choose differently.” That framing is worth more than any perfect textbook answer.
@Component({...})export class ExampleComponent implements OnInit, OnChanges, OnDestroy { // Called once before ngOnInit when inputs change ngOnChanges(changes: SimpleChanges) {} // Called once after first ngOnChanges ngOnInit() {} // Called every change detection cycle ngDoCheck() {} // Called once after first ngDoCheck ngAfterContentInit() {} // Called after every ngDoCheck ngAfterContentChecked() {} // Called once after first ngAfterContentChecked ngAfterViewInit() {} // Called after every ngAfterContentChecked ngAfterViewChecked() {} // Called before component is destroyed ngOnDestroy() {}}
Simpler mental model than RxJS for synchronous state
Lazy evaluation of computed values (only recompute when read AND dependencies changed)
What a senior engineer would add: “Signals are not a replacement for RxJS — they solve different problems. Signals are for synchronous, UI-driven state (a counter, a filter value, a selected item). RxJS is for asynchronous streams (HTTP responses, WebSocket messages, debounced user input). The best Angular codebases use both: signals for component state, RxJS for async pipelines, and toSignal() / toObservable() as bridges between them.”
Q4: Explain the difference between OnPush and Default change detection
Answer
Default Strategy: Angular checks the entire component tree on every change detection cycle.OnPush Strategy: Component only checked when:
Input reference changes
Event from component or child
Async pipe emits new value
Manual markForCheck() or detectChanges()
@Component({ changeDetection: ChangeDetectionStrategy.OnPush})export class OptimizedComponent { data = input<Data>(); // Only triggers on reference change // Manual trigger if needed private cdr = inject(ChangeDetectorRef); forceUpdate() { this.cdr.markForCheck(); }}
Best Practice: Use OnPush everywhere with immutable data patterns.Interview tip: If asked “when would you NOT use OnPush?”, the honest answer is “almost never in new code.” The only exception is rapid prototyping where you are mutating objects in place and do not want to debug why the UI is not updating. But any production code should use OnPush — it turns change detection from “check everything every time” to “check this component only when its inputs change.” That is the difference between O(n) and O(1) per component.
Q11: What is the difference between Subject, BehaviorSubject, and ReplaySubject?
This is one of the most frequently asked RxJS questions. The key mental model: all Subjects are both Observables (you can subscribe to them) and Observers (you can push values into them). They differ in what happens to late subscribers — subscribers that join after values have already been emitted.
Answer
// Subject - No initial value, late subscribers miss past emissionsconst subject = new Subject<number>();subject.next(1);subject.subscribe(v => console.log(v)); // Won't receive 1subject.next(2); // Logs: 2// BehaviorSubject - Has initial/current valueconst behavior = new BehaviorSubject<number>(0);behavior.next(1);behavior.subscribe(v => console.log(v)); // Logs: 1 (current value)behavior.getValue(); // Returns current value: 1// ReplaySubject - Replays N values to new subscribersconst replay = new ReplaySubject<number>(2); // Buffer size 2replay.next(1);replay.next(2);replay.next(3);replay.subscribe(v => console.log(v)); // Logs: 2, 3// AsyncSubject - Only emits last value on completionconst async = new AsyncSubject<number>();async.next(1);async.next(2);async.subscribe(v => console.log(v)); // Nothing yetasync.complete(); // Now logs: 2
The trick to mastering RxJS operators is to group them by what problem they solve, not memorize them alphabetically. In an interview, showing you can pick the right operator for a scenario is far more impressive than listing 20 operator names.
Answer
// Transformationmap(x => x * 2) // Transform each valueswitchMap(x => http.get(x)) // Switch to new observable, cancel previousmergeMap(x => http.get(x)) // Merge all inner observablesconcatMap(x => http.get(x)) // Queue inner observables sequentially// Filteringfilter(x => x > 0) // Filter valuesdistinctUntilChanged() // Only emit when value changesdebounceTime(300) // Wait for pause in emissionsthrottleTime(300) // Rate limit emissionstake(5) // Take first N valuestakeUntil(destroy$) // Take until signal// CombinationcombineLatest([a$, b$]) // Emit when any source emitsforkJoin([a$, b$]) // Emit when all completemerge(a$, b$) // Merge multiple streamswithLatestFrom(other$) // Combine with latest from another// Error HandlingcatchError(err => of(null)) // Handle errorsretry(3) // Retry on errorretryWhen(...) // Custom retry logic// Utilitytap(x => console.log(x)) // Side effectsshareReplay(1) // Share and cache last value
Server-Side Rendering (SSR) renders the application on the server, sending HTML to the client.Hydration reuses the server-rendered DOM instead of re-creating it.
// app.config.server.tsexport const config: ApplicationConfig = { providers: [ provideServerRendering(), provideClientHydration() ]};// Benefits:// - Better SEO (search engines see content)// - Faster First Contentful Paint// - Better Core Web Vitals// - Works with JavaScript disabled// Hydration skipping for dynamic content@Component({ host: { ngSkipHydration: 'true' }})export class DynamicComponent {}