Day 11 β Angular Lifecycle Hooks Deep Dive
π Day 11 β Angular Lifecycle Hooks Deep Dive
Zero to Hero β Hands-on Angular Tutorial
Today you will learn:
- βοΈ The complete Angular Component Lifecycle
- βοΈ
constructorvs.ngOnInit - βοΈ
ngOnDestroy(Cleanup & Memory Leaks) - βοΈ
ngAfterViewInit(Accessing the DOM) - βοΈ
ngOnChangesvs. Signaleffect() - βοΈ Integrating
DestroyRef(Modern Cleanup)
Understanding lifecycle hooks prevents bugs, memory leaks, and performance issues.
π¦ 1. The Lifecycle Overview
Angular components go through stages:
- Creation
- Rendering
- Destruction
Angular gives us βhooksβ (methods) to plug into these moments.
| Hook | Timing | Use Case |
|---|---|---|
constructor | Instantiation | Dependency Injection only. |
ngOnInit | After input bindings | API calls, initialization logic. |
ngOnChanges | When @Input changes | Reacting to data changes (Legacy). |
ngAfterViewInit | After template renders | DOM manipulation, Canvas, Charts. |
ngOnDestroy | Before removal | Cleanup, unsubscribing. |
π© 2. constructor() vs. ngOnInit()
The Golden Rule:
- Constructor: Setup Dependency Injection.
- ngOnInit: Setup Logic/Data.
β BAD:
constructor(private api: ApiService) {
// Don't fetch data here! Inputs aren't ready yet.
this.api.getData().subscribe();
}
β GOOD:
constructor(private api: ApiService) {}
ngOnInit() {
// Inputs are ready. Safe to fetch data.
this.api.getData().subscribe();
}
π§ 3. Cleanup with ngOnDestroy (Crucial!)
If you use setInterval, addEventListener, or manual subscribe(), you MUST clean it up. Otherwise, you get memory leaks.
Legacy Way:
export class Component implements OnDestroy {
intervalId: any;
ngOnInit() {
this.intervalId = setInterval(() => console.log('Tick'), 1000);
}
ngOnDestroy() {
clearInterval(this.intervalId);
console.log('Cleanup done!');
}
}
Modern Way (DestroyRef):
Available in Angular 16+.
import { Component, inject, DestroyRef } from '@angular/core';
@Component({...})
export class ModernComponent {
destroyRef = inject(DestroyRef);
ngOnInit() {
const id = setInterval(() => console.log('Tick'), 1000);
// Register cleanup callback right where you create the side effect
this.destroyRef.onDestroy(() => clearInterval(id));
}
}
π₯ 4. Accessing the DOM: ngAfterViewInit
Sometimes you need to access a generic HTML element (e.g., for a Chart.js canvas or focus management).
Template:
<input #myInput type="text" />
Component:
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
export class FocusComponent implements AfterViewInit {
@ViewChild('myInput') input!: ElementRef;
ngAfterViewInit() {
// DOM is now guaranteed to exist
this.input.nativeElement.focus();
}
}
β οΈ Warning: Do NOT modify data in ngAfterViewInit. It causes the dreaded ExpressionChangedAfterItHasBeenCheckedError.
π« 5. Reacting to Changes: ngOnChanges vs. Signals
Legacy Way (ngOnChanges):
Complex to maintain.
@Input() userId = '';
ngOnChanges(changes: SimpleChanges) {
if (changes['userId']) {
this.loadUser(changes['userId'].currentValue);
}
}
Modern Way (Signal Inputs + Effects): Much cleaner.
userId = input.required<string>();
constructor() {
effect(() => {
// Automatically runs whenever userId() changes
this.loadUser(this.userId());
});
}
π End of Day 11 β What You Learned
Today you mastered the lifecycle:
- βοΈ Initialization: Logic goes in
ngOnInit. - βοΈ Cleanup: Always stop intervals/streams in
ngOnDestroyorDestroyRef. - βοΈ DOM Access: Use
ngAfterViewInit. - βοΈ Reactivity: Prefer Signals/Effects over
ngOnChanges.
You are now writing safe, leak-free code. π‘οΈ
π§ͺ Day 11 Challenge
Build a βCountdown Timerβ Component.
Requirements:
- Accepts an input
seconds(number). - Starts counting down immediately in
ngOnInit. - Updates the title document (Browser Tab Title) every second.
- Crucial: Stops the timer if the user leaves the page component (
ngOnDestroy).