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.
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
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
Core ideaconstructor 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 |
|
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 | Usually runs on first |
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 |
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().
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();
}
}
}
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 |
Reading | You often read default/undefined instead of the parent-provided value | Use |
Accessing | View may not exist yet; | Use |
Using |
| Treat it like constructor-time DI; still do initialization in |
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.
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.
Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.