Angular: Zero to Senior · Lesson 11 of 11
Interview Prep: Senior (50 Q)
50 questions for mid-senior Angular roles. These go beyond the basics — interviewers expect architectural thinking, performance reasoning, and trade-off discussions.
Change Detection & Zone.js
1. How does Angular's default change detection work internally?
Angular wraps all async browser events (clicks, timers, XHR) using zone.js. When zone.js detects that an async operation completed, it notifies Angular to run change detection. Angular then walks the component tree from root to leaves, checking each component for dirty state and updating the DOM.
2. What is zone.js and why is Angular moving away from it?
Zone.js is a library that monkey-patches browser APIs (setTimeout, Promise, XHR, etc.) to intercept async operations. Angular uses it to know when to run change detection. The problem: it's imprecise (checks too much), adds bundle size (~100KB), and makes debugging harder. Angular Signals + zoneless mode (Angular 18+) are the replacement.
3. How do you run code outside of Angular's zone?
constructor(private ngZone: NgZone) {
this.ngZone.runOutsideAngular(() => {
// runs without triggering change detection
setInterval(() => { this.tick++; }, 16);
});
// re-enter when you need the DOM to update
this.ngZone.run(() => this.render());
}Use this for high-frequency events (WebGL render loops, Canvas animations) that shouldn't trigger change detection on every frame.
4. What is the difference between markForCheck() and detectChanges()?
markForCheck() marks the component and its ancestors as dirty — change detection will check them on the next Angular tick. detectChanges() immediately runs change detection on the component subtree synchronously. Use markForCheck() in async callbacks; use detectChanges() when you need immediate synchronous DOM updates.
5. Explain the OnPush contract — what breaks it?
OnPush only re-renders when: input references change, events fire within the component, or markForCheck() is called. It breaks when you mutate an object/array input instead of replacing it. Example: this.items.push(x) won't trigger re-render because the array reference didn't change. Always produce new references: this.items = [...this.items, x].
Signals
6. What problem do Angular Signals solve?
Signals provide fine-grained reactivity without zone.js. A signal knows exactly which components read it — only those components re-render when it changes. This is more precise than zone.js's broad change detection sweeps, enabling zoneless mode with better performance.
7. What is the difference between signal(), computed(), and effect()?
signal(value) is a writable reactive value. computed(() => expression) is a read-only derived value — it recomputes only when its signal dependencies change (memoized). effect(() => sideEffect) runs whenever its signal dependencies change — use for logging, localStorage sync, or DOM operations that can't be done declaratively.
8. When would you use toSignal() vs the async pipe?
toSignal() converts an Observable to a signal — use it when you want to move away from Observable-based templates. The async pipe is still fine and well-understood. Prefer toSignal() when using ChangeDetectionStrategy.OnPush with signals, as it integrates better with the signal-based reactivity model.
Dependency Injection Advanced
9. What are hierarchical injectors in Angular?
Angular has a tree of injectors mirroring the component tree. When a component requests a service, Angular walks up the injector tree until it finds a provider. Services provided in a component are only available to that component and its children. Root services are available everywhere. This enables scoped state per component subtree.
10. What is the difference between providedIn: 'root' and providers in a component?
providedIn: 'root' creates one singleton for the whole app. providers: [ServiceName] in a component creates a new instance scoped to that component. Each <app-shopping-cart> instance would get its own CartService — isolated state per instance.
11. What is a multi-provider? When would you use one?
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }multi: true allows multiple values for the same token. Angular collects all registered values into an array and injects the array. Used for HTTP interceptors, validators, and extension points where multiple providers contribute to the same collection.
12. What is InjectionToken and when do you need it?
When you need to inject a non-class value (config object, string, factory function). InjectionToken<T> creates a typed DI token. Without it, you'd inject by string which isn't type-safe.
RxJS & HTTP
13. Explain the difference between switchMap, mergeMap, concatMap, and exhaustMap.
switchMap— cancels the previous inner observable. Use for search (cancel stale request when user types more).mergeMap— runs all inner observables concurrently. Use for parallel file uploads.concatMap— queues inner observables one at a time. Use for ordered sequential operations.exhaustMap— ignores new emissions while inner observable is active. Use for submit buttons (ignore double-click).
14. How do you prevent memory leaks from subscriptions?
Four approaches: (1) async pipe — auto-unsubscribes. (2) takeUntilDestroyed(destroyRef) operator — Angular 16+. (3) takeUntil(this.destroy$) with a Subject in ngOnDestroy. (4) Store subscription and call unsubscribe() in ngOnDestroy. The async pipe and takeUntilDestroyed are the cleanest.
15. What is a Subject and how is it different from an Observable?
An Observable is unicast — each subscriber gets an independent execution. A Subject is both an Observable and an Observer — it's multicast (all subscribers share the same execution). Use Subject for broadcasting events across components. Use BehaviorSubject when new subscribers need the last emitted value.
16. When would you use BehaviorSubject over a signal for shared state?
BehaviorSubject when you need Observable operators (pipe, combineLatest, etc.) on the state stream, or when the consuming code expects an Observable interface. Signals when you want simpler syntax and better integration with Angular's new reactivity model. Signals don't support operators natively — you'd need toObservable() to compose them.
17. How do you handle optimistic updates in Angular?
- Update state immediately (before API call)
- Fire the API request
- On success: update with server-confirmed data
- On error: rollback to previous state + show error
deleteProduct(id: number): void {
const previousProducts = this.products();
this.products.update(ps => ps.filter(p => p.id !== id)); // optimistic
this.productService.delete(id).subscribe({
error: () => this.products.set(previousProducts), // rollback
});
}NgRx
18. When should you use NgRx vs a service with signals?
Use NgRx when: (1) Multiple unrelated components need the same state. (2) Complex async flows with loading/error/success states. (3) You need time-travel debugging. (4) State mutations need to be auditable (Redux DevTools). Use a service for feature-level state that doesn't need to be global.
19. What is the purpose of NgRx Effects?
Effects handle side effects that actions trigger — typically API calls. They listen to the action stream, run async operations, and dispatch new actions (success or failure). Keeping side effects out of components and reducers makes both more testable and predictable.
20. What are NgRx Selectors and why are they memoized?
Selectors are pure functions that derive data from the store. They're memoized — if the input slice of state hasn't changed, the selector returns the same reference without recomputing. This prevents unnecessary component re-renders and expensive derived calculations.
21. What is the NgRx Signal Store?
A lighter-weight state management solution from the NgRx team built on Angular Signals. It uses signalStore with withState, withMethods, and withComputed. Better for feature-level state; the full NgRx Store is better for app-wide state that needs DevTools and complex effect handling.
Architecture
22. How do you structure a large Angular application?
Feature-based folder structure with lazy-loaded routes:
src/app/
├── core/ ← Singleton services, interceptors, guards
├── shared/ ← Shared components, pipes, directives
├── features/
│ ├── products/ ← Product components, services, routes
│ ├── orders/ ← Order feature, lazy loaded
│ └── admin/ ← Admin feature, lazy loaded
└── app.routes.tsEach feature owns its own routes, components, and services. Nothing in features/ imports from another feature — use core/ or shared/ for cross-cutting concerns.
23. What is the difference between the core module pattern and the shared module pattern?
Core — services that should only be instantiated once (AuthService, HttpInterceptors, AppConfig). Only imported once in the root. Shared — reusable UI components and pipes used in multiple features (ButtonComponent, DatePipe, TruncatePipe). Imported by each feature that needs it.
24. How do you share state between sibling components that have no direct parent-child relationship?
Three options: (1) Lift state to a common ancestor and use @Input/@Output. (2) Shared service with a BehaviorSubject or signal — both components inject the same singleton. (3) NgRx Store for complex cases. Services are the right choice for most situations.
25. What is a barrel file and what are the trade-offs?
A index.ts that re-exports everything from a folder:
export * from './user.component';
export * from './user.service';Pro: Cleaner imports import { UserService } from '@app/users'. Con: Can cause circular dependency issues and slower compilation in large projects. Use sparingly.
Performance
26. What is the Angular build optimizer?
Part of the Angular CLI's production build. It performs tree-shaking (removes unused code), dead code elimination, and scope hoisting. Works best with providedIn: 'root' (tree-shakeable) rather than NgModule providers arrays.
27. How do you profile Angular app performance?
Use the Angular DevTools browser extension — it shows the component tree, change detection cycles, and how long each check takes. Also use Chrome Performance tab and Lighthouse. Look for components that re-render too often and apply OnPush to them.
28. What is deferrable view (@defer) and when should you use it?
@defer lazy-loads a component and its dependencies only when triggered (viewport, interaction, idle, timer, condition). Use for below-the-fold content, heavy data visualizations, or rarely-accessed UI. It reduces the initial JS bundle and improves Time to Interactive.
29. How do you reduce bundle size in Angular?
- Lazy load all feature routes
- Use
@deferfor heavy components - Use
providedIn: 'root'(tree-shakeable) instead of NgModule providers - Import only specific RxJS operators, not the entire library
- Use Angular CDK instead of full Material for individual components
- Analyze with
ng build --stats-json+webpack-bundle-analyzer
30. What is preloading strategy in routing?
By default, lazy modules aren't downloaded until the user navigates there. Preloading downloads them in the background after the initial load. PreloadAllModules preloads everything. A custom strategy can preload only high-priority routes. This balances initial load time with subsequent navigation speed.
Testing
31. What is TestBed in Angular testing?
Angular's testing utility that creates a mini Angular environment for unit tests. It configures the DI container, compiles components, and provides testing equivalents of Angular services (like HttpTestingController).
32. How do you test a component that depends on a service?
Provide a mock service in TestBed.configureTestingModule({ providers: [{ provide: RealService, useValue: mockService }] }). The mock replaces the real implementation so tests don't hit real APIs or databases.
33. What is fixture.detectChanges() and when must you call it?
It triggers Angular's change detection for the component under test, causing the template to render. You must call it after creating the component and after any state changes that should update the DOM.
34. What is HttpTestingController?
A testing utility that replaces HttpClient's actual HTTP calls with a mock. You assert that a specific request was made, then flush it with mock data: req.flush(mockData). Call httpMock.verify() after each test to ensure no unexpected requests were made.
35. How do you test route navigation in components?
Use RouterTestingHarness (Angular 15+) or provide a mock Router with spyOn(router, 'navigate'). For guards, call the guard function directly with a mock ActivatedRouteSnapshot.
Security
36. What is cross-site scripting (XSS) and how does Angular prevent it?
XSS is when malicious scripts are injected into a page. Angular auto-escapes all interpolated values — {{ userInput }} is always treated as text, not HTML. If you need to render HTML, use [innerHTML] with DomSanitizer.sanitize() to strip dangerous content.
37. What is DomSanitizer and when do you use it?
A service that sanitizes potentially unsafe values before using them in the DOM. Use it when you need to bind raw HTML, URLs, or styles from untrusted sources. Mark values as trusted only when you've verified they are safe.
38. How do you prevent CSRF in Angular?
Angular's HttpClient automatically reads the XSRF-TOKEN cookie and sends it as an X-XSRF-TOKEN header on non-GET requests. Your server validates this header. This is the Angular-native CSRF protection — works out of the box with servers that set the XSRF-TOKEN cookie.
Advanced Patterns
39. What are standalone components and why were they introduced?
Standalone components (Angular 14+) declare their own imports instead of registering in NgModules. They reduce boilerplate, make lazy loading simpler (load a single component), improve tree-shaking, and align with modern framework conventions. NgModules still work but are no longer the default.
40. What is a micro-frontend and how does Angular support it?
Micro-frontends split a large SPA into independently deployable pieces owned by different teams. Angular supports this via Module Federation (Webpack 5), where shell apps load remote Angular apps at runtime. The @angular-architects/module-federation library simplifies setup.
41. What is the difference between APP_INITIALIZER and ngOnInit?
APP_INITIALIZER runs before the app bootstraps — useful for loading config from an API before the app renders. ngOnInit runs after a component initializes. If you need auth config or feature flags loaded before any component renders, use APP_INITIALIZER.
42. What are dynamic components and when do you use them?
Creating components programmatically at runtime using ViewContainerRef.createComponent(). Used for modals, tooltips, notifications, and plugin architectures where the component type isn't known at compile time.
43. What is a secondary outlet (named router outlet)?
<router-outlet /> <!-- primary -->
<router-outlet name="sidebar" /> <!-- named -->Named outlets let you render multiple routes simultaneously. Useful for side panels, drawers, or dialogs that have their own URL state.
44. What is the Ivy renderer?
Angular's current (since v9) rendering engine. It replaced ViewEngine with smaller bundles, faster compilation, better tree-shaking, and improved debugging. All modern Angular runs on Ivy.
45. What is ng-container used for?
A logical grouping element that renders nothing in the DOM. Used when you need to apply a structural directive without adding a real DOM element, or to group multiple elements in a *ngFor or @if block.
46. Explain how Angular handles two-way binding with [(ngModel)].
It's syntactic sugar for [ngModel]="value" (ngModelChange)="value = $event". Property binding sets the input's display value; event binding updates the class property when the user types. The banana-in-a-box syntax [()] signals this combination.
47. What is ChangeDetectorRef.detach() and when would you use it?
Detaches the component from Angular's change detection tree entirely — Angular will never check it automatically. You call detectChanges() manually when you know an update is needed. Used for extreme performance optimization in real-time dashboards or games.
48. What is the difference between APP_BASE_HREF and base href in index.html?
<base href="/"> in index.html tells the browser where to resolve relative URLs. APP_BASE_HREF token provides the same value to Angular's router programmatically — useful when the app is deployed at a sub-path.
49. How do you internationalize (i18n) an Angular app?
Two approaches: (1) Angular's built-in i18n with i18n attributes and ng build --localize for build-time translation (one bundle per locale). (2) ngx-translate library for runtime translation (one bundle, loads translations on demand). The built-in approach is more performant; ngx-translate is more flexible.
50. What would you change about an Angular app that loads slowly?
In priority order: (1) Enable lazy loading for all feature routes. (2) Apply @defer to below-the-fold components. (3) Apply OnPush change detection everywhere. (4) Use toSignal() and signals to reduce Observable overhead. (5) Analyze bundle with webpack-bundle-analyzer and remove unused dependencies. (6) Enable HTTP/2, Brotli compression, and CDN for static assets. (7) Add SSR with Angular Universal for faster First Contentful Paint.
Quick Study Guide
Must know for senior:
Zone.js and how Angular 18 moves beyond it
OnPush contract + when it breaks
Signals: signal, computed, effect, toSignal
Hierarchical injectors + multi-providers
switchMap vs exhaustMap vs concatMap (with use cases)
NgRx: Actions → Reducer → Selector → Effect flow
NgRx Signal Store vs full NgRx Store trade-offs
Feature-based folder structure
@defer triggers and use cases
Bundle size reduction techniques
XSS prevention (auto-escaping, DomSanitizer)
Micro-frontend approach (Module Federation)