🎭 Angular Lifecycle Hooks: The Story of a Component’s Life
The Birthday Party Analogy
Imagine you’re planning a birthday party. Every great party has stages: you get the invitation, set up decorations, welcome guests, watch the party unfold, and finally clean up.
Angular components work exactly the same way! They have a lifecycle—moments from birth to death—and Angular gives us special functions called “hooks” to do things at each stage.
Think of hooks like party planners who show up at exactly the right moment to handle their job.
🎬 Lifecycle Hooks Overview
What Are Lifecycle Hooks?
Lifecycle hooks are special methods that Angular calls automatically at specific moments in a component’s life.
Simple Example:
- When a component is born →
ngOnInitruns - When something changes →
ngOnChangesruns - When a component dies →
ngOnDestroyruns
export class PartyComponent {
ngOnInit() {
console.log('Party started!');
}
ngOnDestroy() {
console.log('Party ended!');
}
}
The Lifecycle Order
graph TD A[🎂 Component Created] --> B[ngOnChanges] B --> C[ngOnInit] C --> D[ngDoCheck] D --> E[ngAfterContentInit] E --> F[ngAfterViewInit] F --> G[🔄 Updates Happen] G --> D G --> H[ngOnDestroy] H --> I[💨 Component Gone]
Memory trick: Changes → Init → Check → Content → View → Destroy
🔄 ngOnChanges: “Something Changed!”
What It Does
ngOnChanges is called every time an @Input() property changes. It’s like a doorbell—it rings whenever new data arrives!
When It Runs
- First: Before
ngOnInit(with initial values) - After: Every time an
@Inputvalue changes
Real-Life Example
Imagine a scoreboard that shows player scores:
@Component({
selector: 'app-score',
template: `<h2>Score: {{ score }}</h2>`
})
export class ScoreComponent {
@Input() score: number = 0;
ngOnChanges(changes: SimpleChanges) {
if (changes['score']) {
const old = changes['score'].previousValue;
const now = changes['score'].currentValue;
console.log(`Score: ${old} → ${now}`);
}
}
}
What’s SimpleChanges?
It’s a special object that tells you:
- previousValue: What it was before
- currentValue: What it is now
- firstChange: Is this the first time?
ngOnChanges(changes: SimpleChanges) {
if (changes['score'].firstChange) {
console.log('First score received!');
}
}
🚀 ngOnInit: “Let’s Get Started!”
What It Does
ngOnInit runs once after the component is created and its inputs are set. This is where you do setup work!
When to Use It
- Fetch data from a server
- Set up subscriptions
- Initialize complex logic
Why Not Use the Constructor?
The constructor runs too early—before inputs are ready!
@Component({
selector: 'app-user',
template: `<p>Hello, {{ userName }}</p>`
})
export class UserComponent implements OnInit {
@Input() userId!: number;
userName: string = '';
constructor() {
// ❌ userId is undefined here!
}
ngOnInit() {
// ✅ userId is ready!
this.fetchUser(this.userId);
}
fetchUser(id: number) {
this.userName = `User #${id}`;
}
}
Key Points
| Constructor | ngOnInit |
|---|---|
| Runs first | Runs after inputs set |
| Basic setup | Data fetching |
| No inputs yet | Inputs ready |
🔍 ngDoCheck: “I’m Watching Everything!”
What It Does
ngDoCheck runs during every change detection cycle. It’s like a security guard checking everything constantly!
When It Runs
- After
ngOnChanges - On every click, keystroke, timer, or event
⚠️ Warning: Use Carefully!
This hook runs very often. Heavy code here slows your app!
Real-Life Example
Detecting changes Angular might miss:
export class ListComponent implements DoCheck {
@Input() items: string[] = [];
private oldLength = 0;
ngDoCheck() {
if (this.items.length !== this.oldLength) {
console.log('List changed!');
this.oldLength = this.items.length;
}
}
}
When to Use
- Custom change detection
- Tracking object/array mutations
- Performance monitoring
📦 ngAfterContentInit: “Guests Have Arrived!”
What It Does
ngAfterContentInit runs once after Angular puts external content inside your component using <ng-content>.
What Is ng-content?
It lets you project content from parent to child:
Parent:
<app-card>
<p>I'm projected content!</p>
</app-card>
Child (CardComponent):
<div class="card">
<ng-content></ng-content>
</div>
Example
@Component({
selector: 'app-card',
template: `
<div class="card">
<ng-content></ng-content>
</div>
`
})
export class CardComponent implements AfterContentInit {
@ContentChild('header') header!: ElementRef;
ngAfterContentInit() {
// Projected content is ready!
console.log('Content loaded:', this.header);
}
}
Key Point
Use @ContentChild or @ContentChildren to access projected content.
👁️ ngAfterViewInit: “The Room Is Ready!”
What It Does
ngAfterViewInit runs once after the component’s view (and child views) are fully created.
When to Use It
- Access DOM elements
- Initialize third-party libraries
- Set up canvas or charts
Example
@Component({
selector: 'app-canvas',
template: `<canvas #myCanvas></canvas>`
})
export class CanvasComponent implements AfterViewInit {
@ViewChild('myCanvas') canvas!: ElementRef;
ngAfterViewInit() {
// Canvas is ready!
const ctx = this.canvas.nativeElement
.getContext('2d');
ctx.fillRect(0, 0, 100, 100);
}
}
Content vs View
| ngAfterContentInit | ngAfterViewInit |
|---|---|
| External content | Component’s template |
<ng-content> |
@ViewChild |
| Runs first | Runs after |
💀 ngOnDestroy: “Time to Clean Up!”
What It Does
ngOnDestroy runs once just before Angular destroys the component. This is your chance to clean up!
What to Clean
- Unsubscribe from Observables
- Clear intervals/timeouts
- Remove event listeners
- Disconnect from WebSockets
Example
export class TimerComponent implements OnDestroy {
private subscription!: Subscription;
private intervalId!: number;
ngOnInit() {
this.subscription = someObservable.subscribe();
this.intervalId = setInterval(() => {
console.log('tick');
}, 1000);
}
ngOnDestroy() {
// Clean up!
this.subscription.unsubscribe();
clearInterval(this.intervalId);
console.log('Timer destroyed');
}
}
⚠️ Memory Leaks
If you forget to clean up, your app keeps running old code in the background. This is called a memory leak—it makes your app slow and buggy!
🎯 The Complete Picture
Lifecycle Order Diagram
graph TD A[Constructor] --> B[ngOnChanges] B --> C[ngOnInit] C --> D[ngDoCheck] D --> E[ngAfterContentInit] E --> F[ngAfterContentChecked] F --> G[ngAfterViewInit] G --> H[ngAfterViewChecked] H --> I{Updates?} I -->|Yes| B I -->|Destroy| J[ngOnDestroy]
Quick Reference
| Hook | Runs | Purpose |
|---|---|---|
| ngOnChanges | On input change | React to data |
| ngOnInit | Once, after setup | Initialize |
| ngDoCheck | Every check | Custom detection |
| ngAfterContentInit | Once | Content ready |
| ngAfterViewInit | Once | View ready |
| ngOnDestroy | Once, on death | Clean up |
🧠 Remember This!
The Party Story:
- 📬 ngOnChanges - Invitations arrive (inputs change)
- 🎉 ngOnInit - Party starts (initialization)
- 👀 ngDoCheck - Security checks everyone (detection)
- 🎁 ngAfterContentInit - Gifts placed inside (content projection)
- 🏠 ngAfterViewInit - Room decorated (view ready)
- 🧹 ngOnDestroy - Cleanup crew arrives (destroy)
Now you understand the complete lifecycle of an Angular component. Each hook has its perfect moment—use them wisely, and your components will run smoothly from birth to death! 🚀