Master Angular lifecycle hooks including ngOnInit, ngOnChanges, ngAfterViewInit, and ngOnDestroy for component lifecycle management.
Master Angular lifecycle hooks including ngOnInit, ngOnChanges, ngAfterViewInit, and ngOnDestroy for component lifecycle management.
Learn about Lifecycle Hook Sequence.
constructor:
Component is instantiated
ngOnChanges:
Called when @Input() values change
Called once after first ngOnChanges
ngDoCheck:
Custom change detection
ngAfterContentInit:
After content projection initialized
ngAfterContentChecked:
After content checked
ngAfterViewInit:
After component's view initialized
ngAfterViewChecked:
After view checked
ngOnDestroy:
Before component is destroyed
Learn about ngOnInit - Component Initialization.
1import { Component, OnInit, inject } from '@angular/core';
2import { UserService } from './user.service';
3
4@Component({
5 selector: 'app-user-list',
6 template: \`
7 <div>
8 @for (user of users; track user.id) {
9 <div>{{ user.name }}</div>
10 }
11 </div>
12 \`
13})
14export class UserListComponent implements OnInit {
15 private userService = inject(UserService);
16 users: User[] = [];
17
18 // ✓ DO: Use ngOnInit for initialization logic
19 ngOnInit() {
20 console.log('Component initialized');
21 this.loadUsers();
22 }
23
24 private loadUsers() {
25 this.userService.getUsers().subscribe(users => {
26 this.users = users;
27 });
28 }
29
30 // ✗ DON'T: Avoid complex logic in constructor
31 constructor() {
32 // Only use for dependency injection
33 }
34}Learn about ngOnChanges - Input Property Changes.
1import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
2
3@Component({
4 selector: 'app-user-detail',
5 template: \`<div>{{ displayName }}</div>\`
6})
7export class UserDetailComponent implements OnChanges {
8 @Input() userId!: number;
9 @Input() userName!: string;
10 displayName = '';
11
12 ngOnChanges(changes: SimpleChanges) {
13 console.log('Changes detected:', changes);
14
15 // Check if specific property changed
16 if (changes['userId']) {
17 const change = changes['userId'];
18 console.log('UserId changed:',
19 'Previous:', change.previousValue,
20 'Current:', change.currentValue,
21 'First Change:', change.firstChange
22 );
23 this.loadUserData(change.currentValue);
24 }
25
26 if (changes['userName']) {
27 this.displayName = \`User: \${changes['userName'].currentValue}\`;
28 }
29 }
30
31 private loadUserData(userId: number) {
32 // Fetch user data based on new ID
33 }
34}Learn about ngAfterViewInit - View Access.
1import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
2
3@Component({
4 selector: 'app-search',
5 template: \`
6 <input #searchInput type="text" placeholder="Search...">
7 <app-child-component></app-child-component>
8 \`
9})
10export class SearchComponent implements AfterViewInit {
11 @ViewChild('searchInput') searchInput!: ElementRef<HTMLInputElement>;
12 @ViewChild(ChildComponent) child!: ChildComponent;
13
14 ngAfterViewInit() {
15 // ✓ Safe to access view children here
16 this.searchInput.nativeElement.focus();
17 this.child.initialize();
18
19 // Note: Avoid changing component state here
20 // as it may cause ExpressionChangedAfterItHasBeenCheckedError
21 }
22}Learn about ngOnDestroy - Cleanup.
1import { Component, OnInit, OnDestroy } from '@angular/core';
2import { Subscription, interval } from 'rxjs';
3
4@Component({
5 selector: 'app-timer',
6 template: \`<p>Elapsed: {{ elapsed }}s</p>\`
7})
8export class TimerComponent implements OnInit, OnDestroy {
9 private subscription = new Subscription();
10 elapsed = 0;
11
12 ngOnInit() {
13 // Subscribe to interval
14 this.subscription.add(
15 interval(1000).subscribe(() => {
16 this.elapsed++;
17 })
18 );
19
20 // Subscribe to service data
21 this.subscription.add(
22 this.dataService.data$.subscribe(data => {
23 // Handle data
24 })
25 );
26 }
27
28 ngOnDestroy() {
29 // ✓ Unsubscribe from all observables
30 this.subscription.unsubscribe();
31
32 // ✓ Clear timers/intervals
33 if (this.timerId) {
34 clearInterval(this.timerId);
35 }
36
37 // ✓ Remove event listeners
38 document.removeEventListener('scroll', this.scrollHandler);
39
40 // ✓ Release references
41 this.largeDataStructure = null;
42
43 console.log('Component destroyed - cleaned up');
44 }
45}Learn about ngDoCheck - Custom Change Detection.
1import { Component, DoCheck, Input } from '@angular/core';
2
3@Component({
4 selector: 'app-data-display',
5 template: \`<div>{{ dataString }}</div>\`
6})
7export class DataDisplayComponent implements DoCheck {
8 @Input() data: any[] = [];
9 dataString = '';
10 private previousDataLength = 0;
11
12 ngDoCheck() {
13 // Detect changes that Angular might miss
14 if (this.data.length !== this.previousDataLength) {
15 console.log('Array length changed:', this.data.length);
16 this.dataString = JSON.stringify(this.data);
17 this.previousDataLength = this.data.length;
18 }
19
20 // Warning: This runs on every change detection cycle
21 // Use sparingly for performance
22 }
23}Learn about Content Hooks.
1import { Component, ContentChild, AfterContentInit, AfterContentChecked } from '@angular/core';
2
3@Component({
4 selector: 'app-wrapper',
5 template: \`
6 <div class="wrapper">
7 <ng-content></ng-content>
8 </div>
9 \`
10})
11export class WrapperComponent implements AfterContentInit, AfterContentChecked {
12 @ContentChild('contentRef') content!: ElementRef;
13
14 ngAfterContentInit() {
15 // Called once after content projection initialized
16 console.log('Content initialized:', this.content);
17 }
18
19 ngAfterContentChecked() {
20 // Called after every check of projected content
21 // Use sparingly - runs frequently
22 }
23}Use ngOnInit for component initialization, not constructor
Always implement ngOnDestroy to prevent memory leaks
Access @ViewChild/@ViewChildren only after ngAfterViewInit
Use ngOnChanges to react to @Input() property changes
Avoid expensive operations in ngDoCheck and ngAfterViewChecked
Unsubscribe from observables in ngOnDestroy
Use DestroyRef and takeUntilDestroyed() in Angular 16+
Avoid changing state in ngAfterViewInit to prevent errors
Log lifecycle hooks during development for debugging
Lifecycle hooks provide precise control over component behavior at different lifecycle stages
ngOnInit is called once after first ngOnChanges, ideal for initialization
ngOnChanges receives SimpleChanges object with previous and current values
ngAfterViewInit is required to safely access @ViewChild and @ViewChildren
ngOnDestroy is critical for cleanup to prevent memory leaks
ngDoCheck enables custom change detection but should be used sparingly
Constructor should only contain dependency injection, not business logic
Content hooks (ngAfterContentInit/Checked) relate to ng-content projection
Understanding lifecycle hooks is essential for optimizing performance and preventing bugs
Your progress is saved in this browser, on this device only.