Angular lifecycle hooks are framework callbacks on components/directives that run at specific moments (creation, input changes, content/view init + checks, destruction). Interviewers care less about memorizing names and more about choosing the correct hook for inputs, DOM/ViewChild, content projection, change-detection edge cases, and cleanup. Framework focus: Angular templates + standalone components, @Input/@Output, and RxJS/async pipe patterns.
Angular lifecycle hooks: when do they run, and what should you put in each?
Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.
Core idea
Hooks are Angular-called methods that let you run code at predictable lifecycle points. The practical skill is picking the right hook:
• Inputs changed? ngOnChanges
• Initialize once? ngOnInit
• Projected content? ngAfterContent*
• DOM / ViewChild? ngAfterViewInit
• Cleanup? ngOnDestroy
Hook | When it runs | Frequency | What it’s for |
|---|---|---|---|
| Class instantiation (before Angular bindings) | Once | DI + minimal field setup. No inputs, no DOM. |
| After an | Many times (or zero if no inputs) | React to input changes; validate/normalize; derive state from inputs. |
| After first | Once | Initialization that depends on inputs/services; start streams; fetch data. |
| During every change detection run | Many times | Rare: custom change tracking when default checks aren’t enough (use carefully). |
| After projected content ( | Once | Work that needs |
| After every check of projected content | Many times | Rare: respond to projected content changes (avoid heavy work). |
| After component view + child views are created | Once | Safe DOM/ViewChild access; measure/focus; init 3rd-party widgets. |
| After every check of the component view | Many times | Rare: respond after view checks (avoid state writes here). |
| Right before Angular destroys the instance | Once | Cleanup: subscriptions, timers, listeners, 3rd-party teardown. |
Scenario | Correct hook | Why |
|---|---|---|
Initialize streams / fetch data |
| Runs once when bindings are ready. |
Derive state from |
| You get |
Access |
| View is guaranteed to exist (default |
Access projected content via |
| Projected content is initialized (different from the view). |
Tear down subscriptions, intervals, 3rd-party widgets |
| Last guaranteed cleanup point. |
import {
AfterContentChecked,
AfterContentInit,
AfterViewChecked,
AfterViewInit,
Component,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
ViewChild,
ElementRef,
} from '@angular/core';
@Component({
selector: 'app-life',
template: `
<p #p>Hello</p>
<ng-content></ng-content>
`
})
export class LifeComponent
implements
OnChanges,
OnInit,
AfterContentInit,
AfterContentChecked,
AfterViewInit,
AfterViewChecked,
OnDestroy {
@Input() value = 0;
@ViewChild('p') p?: ElementRef<HTMLParagraphElement>;
constructor() {
console.log('constructor');
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['value']) {
console.log('ngOnChanges', changes['value'].previousValue, '->', changes['value'].currentValue);
}
}
ngOnInit(): void {
console.log('ngOnInit');
}
ngAfterContentInit(): void {
console.log('ngAfterContentInit');
}
ngAfterContentChecked(): void {
console.log('ngAfterContentChecked');
}
ngAfterViewInit(): void {
console.log('ngAfterViewInit');
// Safe: ViewChild available (default static:false)
this.p?.nativeElement.focus?.();
}
ngAfterViewChecked(): void {
console.log('ngAfterViewChecked');
}
ngOnDestroy(): void {
console.log('ngOnDestroy');
}
}
Execution order (practical)
First render (typical):constructor → ngOnChanges? → ngOnInit → ngAfterContentInit → ngAfterContentChecked → ngAfterViewInit → ngAfterViewChecked
Later change-detection cycles:ngOnChanges? → ngDoCheck → ngAfterContentChecked → ngAfterViewChecked
Note: ngOnChanges only runs for @Input changes from the parent binding; it won’t fire just because you mutated an object in-place without changing the input reference.
Pitfall (interview-grade) | What happens | Fix |
|---|---|---|
Mutating bindings in | Dev mode can throw | Avoid sync bound writes there; schedule (microtask) or refactor logic earlier. |
Forgetting cleanup | Intervals/subscriptions keep running after navigation | Use |
Heavy work in | Runs often → perf issues, jank | Keep these hooks rare and cheap; prefer pure derivations and OnPush patterns. |
Expecting | No call if input reference doesn’t change | Use immutable updates (new reference) or manual detection logic if needed. |
Confusing content vs view | Using | Use |
import { Component, DestroyRef, inject, OnInit } from '@angular/core';
import { interval } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
selector: 'app-clean',
template: `...`
})
export class CleanComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef);
ngOnInit(): void {
// Auto-cleanup when the component is destroyed.
interval(1000)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(v => console.log(v));
}
}
Lifecycle hooks are Angular callbacks around creation, binding updates, view/content init + checks, and teardown. For interviews: know ngOnChanges (inputs), ngOnInit (init once), ngAfterViewInit (DOM/ViewChild), and ngOnDestroy (cleanup), plus the content-vs-view distinction and the common pitfalls.