Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

Lifecycle Hooks in Angular

What are Lifecycle Hooks?

Angular calls lifecycle hook methods at specific moments in a component's life — from creation to destruction. Implementing these interfaces lets you tap into those moments to run your own logic.

Lifecycle Sequence

HookWhen it runsCommon use
ngOnChanges()Before ngOnInit, every time an @Input changesReact to input changes
ngOnInit()Once, after first ngOnChangesFetch data, initialize state
ngDoCheck()Every change detection cycleCustom change detection
ngAfterContentInit()Once, after content projectionAccess @ContentChild
ngAfterContentChecked()After every content checkRespond to projected content changes
ngAfterViewInit()Once, after view & child views initAccess @ViewChild, DOM manipulation
ngAfterViewChecked()After every view checkRespond to view changes
ngOnDestroy()Just before component is destroyedUnsubscribe, cleanup timers

ngOnInit — Most Common Hook

ngOnInit
import { Component, OnInit, inject, signal } from '@angular/core';
import { UserService } from './user.service';

@Component({
    selector: 'app-user',
    standalone: true,
    template: `
        @if (user()) {
            <h2>{{ user()!.name }}</h2>
        } @else {
            <p>Loading...</p>
        }
    `
})
export class UserComponent implements OnInit {
    private userService = inject(UserService);
    user = signal<any>(null);

    ngOnInit() {
        // Runs once after component is initialized
        // Inputs are available here (unlike the constructor)
        this.userService.getUser(1).subscribe(u => this.user.set(u));
    }
}

ngOnChanges — Reacting to Input Changes

ngOnChanges
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({
    selector: 'app-child',
    standalone: true,
    template: `<p>{{ title }}</p>`
})
export class ChildComponent implements OnChanges {
    @Input() title = '';
    @Input() count = 0;

    ngOnChanges(changes: SimpleChanges) {
        if (changes['title']) {
            const prev = changes['title'].previousValue;
            const curr = changes['title'].currentValue;
            console.log(`title changed: ${prev} → ${curr}`);
        }
        if (changes['count']?.firstChange) {
            console.log('count set for the first time:', this.count);
        }
    }
}
// Note: with signal-based input(), use effect() instead of ngOnChanges

ngOnDestroy — Cleanup

ngOnDestroy
import { Component, OnInit, OnDestroy, signal } from '@angular/core';
import { Subscription, interval } from 'rxjs';

@Component({
    selector: 'app-timer',
    standalone: true,
    template: `<p>Elapsed: {{ seconds() }}s</p>`
})
export class TimerComponent implements OnInit, OnDestroy {
    seconds = signal(0);
    private sub!: Subscription;

    ngOnInit() {
        this.sub = interval(1000).subscribe(() => {
            this.seconds.update(s => s + 1);
        });
    }

    ngOnDestroy() {
        // ALWAYS unsubscribe to prevent memory leaks
        this.sub.unsubscribe();
        console.log('TimerComponent destroyed, subscription cleaned up');
    }
}

ngAfterViewInit — Accessing the DOM

ngAfterViewInit
import { Component, AfterViewInit, viewChild, ElementRef } from '@angular/core';

@Component({
    selector: 'app-focus',
    standalone: true,
    template: `<input #nameInput type="text" placeholder="Auto-focused" />`
})
export class FocusComponent implements AfterViewInit {
    // Signal-based ViewChild (Angular 17+)
    nameInput = viewChild.required<ElementRef>('nameInput');

    ngAfterViewInit() {
        // DOM is fully rendered — safe to access elements
        this.nameInput().nativeElement.focus();
    }
}

Modern Alternative: effect() with Signals

With signal-based inputs (input()), you can use effect() instead of ngOnChanges for a cleaner reactive approach.

effect() vs ngOnChanges
import { Component, input, effect, computed } from '@angular/core';

@Component({
    selector: 'app-modern',
    standalone: true,
    template: `<p>{{ greeting() }}</p>`
})
export class ModernComponent {
    name = input.required<string>();

    greeting = computed(() => `Hello, ${this.name()}!`);

    constructor() {
        // Runs whenever name() changes — replaces ngOnChanges
        effect(() => {
            console.log('Name changed to:', this.name());
        });
    }
}

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.