Angular calls a set of lifecycle hook methods on components and directives at specific moments — during creation, every change detection cycle, and finally before destruction. Implementing the corresponding interface (e.g. OnInit, OnDestroy) is optional but recommended for type safety.
1. constructor vs. ngOnInit
The constructor is a TypeScript feature, not an Angular lifecycle hook. Angular calls it before inputs are set, so @Input() values are undefined at that point.
@Component({ selector: 'app-card', template: '' })
export class CardComponent implements OnInit {
@Input() title = '';
constructor() {
console.log(this.title); // always '' — input not yet set
}
ngOnInit() {
console.log(this.title); // correct value, inputs are initialized
}
}
Rule: Use the constructor only for dependency injection. Use ngOnInit for any initialization logic that depends on inputs or injected services.
2. Lifecycle hooks in order
ngOnChanges(changes: SimpleChanges)
Runs whenever one or more @Input() values change. On the first render it runs before ngOnInit. The changes argument is a map of input name → SimpleChange object, each containing previousValue, currentValue, and firstChange.
ngOnChanges(changes: SimpleChanges) {
if (changes['title']) {
const { previousValue, currentValue, firstChange } = changes['title'];
console.log(`title changed from "${previousValue}" to "${currentValue}"`);
}
}
Only implements when the component has @Input() properties. Not called if the component has no inputs.
ngOnInit()
Runs once, after all inputs have been set for the first time and ngOnChanges has been called for the first time (if applicable). The component's own template is not yet rendered at this point, but it is safe to read inputs and call services.
ngOnInit() {
this.data = this.dataService.load(this.id);
}
ngDoCheck()
Runs on every change detection cycle that involves this component — which is very frequent. Use it only when you need to detect changes Angular's default change detection misses (e.g. mutated object references). Avoid heavy logic here.
ngDoCheck() {
// runs on every CD cycle — keep this extremely lightweight
if (this.items !== this.previousItems) {
this.previousItems = this.items;
this.recompute();
}
}
ngAfterContentInit()
Runs once after Angular projects external content into the component's <ng-content> slots. This is where you can safely read @ContentChild / @ContentChildren queries.
@ContentChild(HeaderComponent) header!: HeaderComponent;
ngAfterContentInit() {
console.log(this.header); // projected content is available here
}
ngAfterContentChecked()
Runs after every change detection check of the projected content, including the first. Rarely needed — prefer ngAfterContentInit for one-time setup.
ngAfterViewInit()
Runs once after the component's own template (and all child components within it) has been fully initialized. This is where you can safely read @ViewChild / @ViewChildren queries.
@ViewChild('canvas') canvasRef!: ElementRef<HTMLCanvasElement>;
ngAfterViewInit() {
const ctx = this.canvasRef.nativeElement.getContext('2d');
// safe to access DOM here
}
ngAfterViewChecked()
Runs after every change detection check of the component's view. Rarely needed. Be careful — modifying component state here can cause ExpressionChangedAfterItHasBeenChecked errors.
ngOnDestroy()
Runs once, just before Angular removes the component from the DOM. Use it to clean up anything that would otherwise cause memory leaks: timers, subscriptions, event listeners.
export class PollingComponent implements OnInit, OnDestroy {
private intervalId!: ReturnType<typeof setInterval>;
ngOnInit() {
this.intervalId = setInterval(() => {
this.poll();
}, 3000);
}
ngOnDestroy() {
clearInterval(this.intervalId); // without this the timer keeps running after the component is gone
}
private poll() { /* ... */ }
}
3. DestroyRef — modern alternative to ngOnDestroy (Angular 16+)
DestroyRef is an injectable token that lets you register a cleanup callback anywhere in the component — right next to the setup code, rather than in a separate ngOnDestroy method. This makes the code easier to follow.
export class PollingComponent implements OnInit {
private destroyRef = inject(DestroyRef);
ngOnInit() {
const intervalId = setInterval(() => {
this.poll();
}, 3000);
this.destroyRef.onDestroy(() => {
clearInterval(intervalId); // cleanup lives next to setup
});
}
private poll() { /* ... */ }
}
DestroyRef also works well with RxJS via the takeUntilDestroyed operator from @angular/core/rxjs-interop:
import { Component, inject, OnInit } from '@angular/core';
import { DestroyRef } from '@angular/core';
import { interval } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({ ... })
export class PollingComponent implements OnInit {
private destroyRef = inject(DestroyRef);
ngOnInit() {
interval(3000)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => this.poll());
}
private poll() { /* ... */ }
}
The subscription is automatically cancelled when the component is destroyed — no ngOnDestroy needed.
4. Execution order
First render
| Order | Hook | Notes |
|---|---|---|
| 1 | constructor |
DI only — inputs not yet set |
| 2 | ngOnChanges |
Only if the component has @Input() properties |
| 3 | ngOnInit |
Inputs are ready |
| 4 | ngDoCheck |
|
| 5 | ngAfterContentInit |
Projected content is ready |
| 6 | ngAfterContentChecked |
|
| 7 | ngAfterViewInit |
Component's view and child views are ready |
| 8 | ngAfterViewChecked |
Every subsequent change detection cycle
| Order | Hook | Notes |
|---|---|---|
| 1 | ngOnChanges |
Only if an @Input() value changed |
| 2 | ngDoCheck |
|
| 3 | ngAfterContentChecked |
|
| 4 | ngAfterViewChecked |
On destruction
| Order | Hook |
|---|---|
| 1 | ngOnDestroy |
5. Which hooks are used most often?
| Hook | How common | Typical use |
|---|---|---|
ngOnInit |
Very common | Load data, read inputs, subscribe to services |
ngOnChanges |
Common | React to input changes |
ngOnDestroy / DestroyRef
|
Common | Clean up timers, subscriptions |
ngAfterViewInit |
Occasional | Access DOM refs, initialize third-party libraries |
ngAfterContentInit |
Occasional | Access projected content queries |
ngDoCheck |
Rare | Fine-grained change detection |
ngAfterViewChecked |
Rare | Post-render DOM reads |
ngAfterContentChecked |
Rare | Post-check projected content |
Top comments (0)