Angular Interview Questions and Answers

Components, dependency injection, RxJS, signals and Angular tooling.

Practise 10 random 10 peer-reviewed questions
Angular Interview Syllabus & Preparation Strategy

Whether you are preparing for entry-level Angular interview questions for freshers or senior software engineer interview questions addressing concurrency, scalability, and system architecture, this track provides peer-reviewed model answers with syntax walkthroughs, edge cases, and practical interview tips.

1 What is the order of Angular lifecycle hooks? Easy

Angular calls lifecycle hooks in a defined order. The constructor runs first and should only inject dependencies. Then ngOnChanges fires whenever a bound input changes, before ngOnInit, which runs once after the first ngOnChanges. After that comes ngOnDestroy. In between, ngOnDestroy comes after view checks: specifically ngOnInit, then ngDoCheck on every change-detection run, followed by ngAfterContentInit and ngAfterContentChecked for projected content, then ngAfterViewInit and ngAfterViewChecked for the component's own view. ngOnDestroy runs once just before the component is removed and is where you unsubscribe, clear timers and detach listeners.

Order matters: ViewChild results are only available in ngAfterViewInit unless the query is static: true. Child views are initialised before the parent's ngAfterViewInit, so the parent hook runs last. Implement the typed interfaces such as OnInit for safety and clarity.

2 What are the different types of data binding in Angular? Easy

Angular has several binding forms. Interpolation {{ value }} renders a component property into text. Property binding [property]="expr" sets a DOM property or directive input. Attribute binding [attr.aria-label]="expr" sets an attribute when no matching property exists.

Event binding (click)="handler($event)" listens for events, and two-way binding [(ngModel)]="name" combines a value input with a valueChange output; it is sugar for [value]="name" (valueChange)="name = $event".

Class and style bindings use [class.active], [ngClass], [style.width.px] and [ngStyle]. Template reference variables such as #input let you read an element locally, and the $event variable exposes the event payload.

Data flows down through inputs and up through outputs, which is the core mental model. Inputs should be treated as read-only in the child, and the parent owns the state.

3 How does dependency injection work in Angular? Medium

Dependency injection supplies instances to classes instead of letting them construct collaborators. Providers are registered at the root (providedIn: 'root'), on a route, or in a component's providers array. The injector resolves a token by walking up the hierarchy from the requesting component, so a component-level provider creates a new instance per subtree.

@Injectable({ providedIn: 'root' })
export class UserService { constructor(private http: HttpClient) {} }

export class ProfileComponent { private svc = inject(UserService); }

Tokens can be classes, InjectionTokens or strings. useClass, useValue, useFactory and useExisting control how a token is satisfied, which is how you swap implementations in tests.

Hierarchical injection is what makes tree-shakable services and per-route state possible. providedIn: 'root' lets the bundler drop a service that no one injects, while a component provider scopes lifetime to that component.

4 What is the difference between ViewChild and ContentChild? Medium

Both are query decorators, but they search different places. @ViewChild queries the component's own template, while @ContentChild queries content projected into the component through ng-content.

@Component({ selector: 'app-tabs', template: '<ng-content></ng-content>' })
export class TabsComponent {
  @ContentChildren(TabComponent) tabs!: QueryList<TabComponent>;
  @ViewChild('panel') panel!: ElementRef;
}

So a tabs component uses ContentChild to find the tabs a consumer projected, and ViewChild to find elements it owns itself. ViewChildren and ContentChildren return a QueryList that updates as the template changes.

Queries accept a local template reference string, a directive or component type, and options { read, static }. static: true resolves the query before ngAfterViewInit or ngAfterContentInit and is required if you need the result in ngOnInit; otherwise results are set later. In newer Angular, the signal-based viewChild() and contentChild() functions are the recommended alternative.

5 How do Observables differ from Promises, and how do you avoid subscription leaks? Medium

A Promise represents a single future value and is eager: it starts when created, cannot be cancelled, and has no operators beyond then and catch. An Observable is lazy, emits zero, one or many values over time, can be cancelled with unsubscribe, and composes through RxJS operators.

this.http.get<User[]>('/api/users').pipe(
  map(users => users.filter(u => u.active)),
  catchError(() => of([])),
).subscribe(users => this.users = users);

Operators like debounceTime, switchMap, distinctUntilChanged and combineLatest make complex async flows declarative. Angular's HttpClient returns cold observables that fire on subscribe, so forgetting to subscribe means no request.

Subscription leaks are a common bug. Prefer the async pipe, which unsubscribes automatically, or use takeUntilDestroyed, takeUntil with a destroy subject, or firstValueFrom for one-off requests. Never leave a bare long-lived subscribe in a component that never cleans up.

6 When should you use the OnPush change detection strategy? Medium

With the default strategy Angular checks a component on every change-detection run triggered by any event, timer or XHR inside the zone. ChangeDetectionStrategy.OnPush tells Angular to skip the component unless one of its inputs changes by reference, an event originates from the component or its children, an observable bound with the async pipe emits, or you call markForCheck. This dramatically reduces checks in large apps because whole subtrees can be skipped.

@Component({ changeDetection: ChangeDetectionStrategy.OnPush })

Pitfalls: mutating an object in place does not change its reference, so OnPush inputs will not update; create a new array or object instead, or use signals. Async work outside Angular's zone, such as a custom WebSocket callback, will not trigger detection, so call markForCheck or run inside NgZone.run.

OnPush pairs well with immutable data, the async pipe and signals, and it is the default expectation for high-performance Angular components.

7 What is the difference between standalone components and NgModules? Medium

Traditional Angular organises components, directives and pipes into NgModules that declare what belongs together and import other modules. Standalone components skip that: standalone: true, now the default, lets a component import exactly the dependencies it needs directly in its imports array.

@Component({
  standalone: true,
  imports: [RouterLink, CommonModule],
  template: `...`,
})
export class HomeComponent {}

Benefits: less boilerplate, clearer dependency graphs, easier lazy loading because a route can directly loadComponent, better tree-shaking, and a simpler mental model. NgModules still exist for compatibility and for grouping providers or legacy libraries.

The bootstrap API shifted from platformBrowserDynamic().bootstrapModule to bootstrapApplication(AppComponent, { providers: [...] }). Most new Angular projects are fully standalone, and migration is incremental because standalone and module-based declarations interoperate through imports.

8 How do Angular route guards and lazy loading work together? Medium

The router supports guards that run during navigation: CanActivate, CanActivateChild, CanDeactivate for unsaved forms, CanMatch for feature flags, and Resolve to prefetch data. A guard can return a boolean, an Observable, a Promise, or a UrlTree to redirect. Functional guards can use inject().

export const authGuard: CanActivateFn = () => {
  const auth = inject(AuthService);
  const router = inject(Router);
  return auth.isLoggedIn() ? true : router.parseUrl('/login');
};

Lazy loading uses loadChildren for a group of routes or loadComponent for a single standalone component, so the browser downloads that chunk only when the route is visited. Combine it with preloadingStrategy: PreloadAllModules to fetch chunks after the initial render.

Always enforce authorisation on the server as well; client guards improve UX but are not a security boundary. Keep guards small and side-effect free, and return a redirect tree rather than navigating imperatively inside the guard.

9 What are Angular signals and how do they change reactivity? Hard

Signals are Angular's fine-grained reactive primitive. A signal(0) holds a value; reading it with count() registers the consumer as a dependency, and writing with count.set(1) or count.update(v => v + 1) marks dependents dirty. computed() derives a memoised value that recomputes lazily, and effect() runs a side effect whenever its tracked signals change.

const price = signal(10);
const qty = signal(2);
const total = computed(() => price() * qty());
effect(() => console.log(total()));

Unlike zone-based change detection, signals let Angular know exactly which views depend on which state and update only those, enabling zoneless applications. Signal inputs (input()), queries (viewChild()) and model() integrate them with components. untracked() reads without registering a dependency, and linkedSignal or resource handle derived and async cases.

Avoid writing signals inside computed, since computeds must be pure, and use effect sparingly to synchronise with non-reactive APIs. Signals are the direction Angular is moving toward.

10 How does zone.js drive change detection and what changes with zoneless Angular? Hard

Historically Angular relied on zone.js, which monkey-patches async APIs such as setTimeout, promises, XHR and event listeners so the framework knows when something might have changed. Every async callback triggers ApplicationRef.tick(), which walks the component tree from the root; OnPush lets Angular prune subtrees whose inputs are unchanged.

Implications: frequent async work can cause many full checks, and work scheduled outside the zone, such as in a Web Worker callback or a non-patched library, will not update the UI unless you call NgZone.run or markForCheck. Conversely runOutsideAngular avoids needless detection for scroll or mousemove handlers.

this.zone.runOutsideAngular(() => {
  el.addEventListener('scroll', onScroll, { passive: true });
});

Zoneless change detection removes the dependency and uses signals and explicit notification to schedule updates, improving performance and debuggability. The trade-off is that any non-signal async mutation must notify Angular explicitly.

Frequently Asked Questions About Angular Interviews

What do hiring managers evaluate in Angular technical rounds?

Technical interviewers look for foundational fluency, idiomatic syntax, clarity when communicating complex logic, and awareness of performance trade-offs (e.g. memory footprint, render performance, and network latency) in production environments.

What are the best interview tips for practicing Angular questions?

Use active recall: summarize each answer in your own words before revealing the model solution. Focus on explaining why a certain approach is chosen rather than just memorizing code syntax.