Interview answer drill

Use this Angular interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.

Constructor vs ngOnInit(): DI timing, @Input timing, and what belongs whereFrontend interview answer

HighIntermediateAngular
Interview focus

This Angular interview question tests whether you can explain Constructor vs ngOnInit in Angular: what is the difference, connect it to production trade-offs, and handle common follow-up questions.

  • Constructor vs ngOnInit in Angular: what is the difference explanation without falling back to memorized docs wording
  • Lifecycle and Hooks reasoning, edge cases, and production failure modes
  • How you would answer the most likely Angular interview follow-up
Practice more Angular interview questions
Interview quick answer

Constructor runs when the class instance is created (mainly for DI + trivial field setup). ngOnInit() runs once after Angular has set initial @Input() bindings (after the first ngOnChanges). The stronger interview answer also calls out test timing, constructor side effects, and when ngOnChanges must take over because the input can change again later.

Full interview answer

Core idea

constructor is a TypeScript/class instantiation step (good for dependency injection + trivial setup). ngOnInit() is an Angular lifecycle hook (good for initialization that depends on Angular bindings like @Input(), and for starting side effects like data loading).

Runs when

Typical first-render order

Component creation

constructor → ngOnChanges? → ngOnInit → ngAfterViewInit

Constructor is earliest; ngOnInit happens after the first input binding pass.

Aspect

constructor

ngOnInit()

Who calls it?

TypeScript runtime (class instantiation)

Angular (lifecycle hook)

Dependency injection

✅ Yes (primary use)

✅ Yes (DI already done)

@Input() values available?

❌ Not reliably (bindings not applied yet)

✅ Yes (initial bindings are applied)

Test timing

Runs during TestBed.createComponent()

Usually runs on first fixture.detectChanges()

Good for

Assigning injected services to fields; lightweight defaults

Starting streams, fetching data, initializing based on inputs, wiring subscriptions

Avoid

Heavy work, subscriptions, API calls, reading inputs, touching DOM/ViewChild

Direct DOM/ViewChild access (usually use ngAfterViewInit)

What belongs where (interview version)

Why interviewers care

Putting side effects in the constructor makes component instantiation do "real work" before Angular finishes binding and before tests can set up spies. Putting Angular-dependent logic in ngOnInit is predictable: bindings are ready, and in tests it usually runs on fixture.detectChanges().

TYPESCRIPT
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';

@Component({
  selector: 'app-user-details',
  template: `User id: {{ userId }}`
})
export class UserDetailsComponent implements OnInit, OnChanges {
  @Input() userId!: string;

  constructor(private readonly api: UsersApi) {
    // ❌ Wrong: constructor runs before Angular finishes the first input binding pass.
    // this.api.loadUser(this.userId).subscribe();
  }

  ngOnInit(): void {
    // ✅ First load when the initial input is ready.
    this.api.loadUser(this.userId).subscribe();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['userId'] && !changes['userId'].firstChange) {
      // ✅ Follow-up when the parent changes the input again later.
      this.api.loadUser(this.userId).subscribe();
    }
  }
}
                  
TYPESCRIPT
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';

@Component({
  selector: 'app-user',
  template: `User id: {{ userId }}`
})
export class UserComponent implements OnChanges, OnInit {
  @Input() userId!: string;

  constructor() {
    // At this point Angular hasn't applied @Input bindings yet.
    console.log('constructor userId =', this.userId); // often undefined
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['userId']) {
      console.log('ngOnChanges userId =', changes['userId'].currentValue);
    }
  }

  ngOnInit(): void {
    // Safe: initial @Input bindings already applied.
    console.log('ngOnInit userId =', this.userId);
  }
}
                  

Common pitfall

What breaks

Fix

Calling APIs/subscribing in the constructor

Runs before inputs are set; harder to test (spies not set up yet); surprise side effects on instantiation

Move to ngOnInit (or a facade/service triggered from init)

Reading @Input() in the constructor

You often read default/undefined instead of the parent-provided value

Use ngOnInit for initial value; use ngOnChanges (or an input setter) for reacting to changes

Accessing @ViewChild / DOM in ngOnInit

View may not exist yet; ViewChild can be undefined (default static:false)

Use ngAfterViewInit (or @ViewChild({ static: true }) only when the element is always present)

Using inject() and assuming it behaves like ngOnInit

inject() runs during instantiation (same timing category as constructor)

Treat it like constructor-time DI; still do initialization in ngOnInit

What senior candidates mention

Practical notes

Watch for edge case behavior, common pitfalls, and trade-offs between clarity and performance. Mention accessibility and testing considerations when the concept affects UI output or event timing.

Summary

constructor = DI + trivial setup (no inputs, no side effects). ngOnInit = initialization that depends on Angular bindings (@Input) and starting side effects (streams/data loading). If you must react to input changes over time, use ngOnChanges (or an input setter). DOM/ViewChild work usually belongs in ngAfterViewInit.

Similar questions
Guides
Preparing for interviews?

Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.