Explain Angular component lifecycle hooks in detail.
Angular components have a well-defined lifecycle managed by Angular itself. Lifecycle hooks allow developers to tap into key moments during a component's creation, update, and destruction, enabling fine-grained control over component behavior.
Overview
Angular provides a set of interfaces, known as lifecycle hooks, that you can implement in your components and directives. These interfaces declare methods that Angular calls at specific points in the component's lifecycle. By implementing these methods, you can execute custom logic at the right time, such as initializing data, performing DOM manipulation, or cleaning up resources.
Core Lifecycle Hooks
ngOnChanges
Called before ngOnInit (if the component has bound inputs) and whenever one or more data-bound input properties change. It receives a SimpleChanges object containing the current and previous property values. This hook is ideal for reacting to changes in input properties and performing actions based on those changes.
import { Component, OnChanges, Input, SimpleChanges } from '@angular/core';
@Component({
selector: 'app-child',
template: '<p>Name: {{ name }}</p>'
})
export class ChildComponent implements OnChanges {
@Input() name: string;
ngOnChanges(changes: SimpleChanges): void {
if (changes['name']) {
console.log('Name changed from', changes['name'].previousValue, 'to', changes['name'].currentValue);
}
}
}
ngOnInit
Called once after the first ngOnChanges. Initializes the component after Angular sets the component's input properties and checks the first content. It's the most commonly used hook for initial setup, data fetching from a service, or complex initialization logic that doesn't rely on input changes.
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-my-component',
template: '<p>Component initialized!</p>'
})
export class MyComponent implements OnInit {
ngOnInit(): void {
console.log('ngOnInit: Component has been initialized.');
// Perform data fetching or other setup here
}
}
ngDoCheck
Called immediately after ngOnChanges and ngOnInit, and then after every subsequent ngOnChanges. It is also called after every change detection run, even if no inputs have changed. This hook allows you to implement your own custom change detection logic or to detect changes that Angular might not catch automatically (e.g., changes within objects or arrays passed as inputs without changing their reference). Use with caution as it can impact performance if not optimized.
import { Component, DoCheck } from '@angular/core';
@Component({
selector: 'app-do-check-component',
template: '<p>DoCheck component</p>'
})
export class DoCheckComponent implements DoCheck {
ngDoCheck(): void {
console.log('ngDoCheck: Always checking for changes.');
// Custom change detection logic here
}
}
ngAfterContentInit
Called once after Angular projects external content into the component's view (content projected via <ng-content>). This hook is useful for performing initialization logic after all projected content has been initialized. It's specifically for content that is 'transcluded' into the component.
import { Component, AfterContentInit, ContentChild, ElementRef } from '@angular/core';
@Component({
selector: 'app-content-parent',
template: `
<div #projectedContent>
<ng-content></ng-content>
</div>
`
})
export class ContentParentComponent implements AfterContentInit {
@ContentChild('projectedContent') projectedDiv: ElementRef;
ngAfterContentInit(): void {
console.log('ngAfterContentInit: Projected content initialized.', this.projectedDiv.nativeElement.textContent);
}
}
ngAfterContentChecked
Called after every ngDoCheck and after the content of the component has been checked. This hook is useful for performing actions that need to happen after Angular has checked the projected content for changes. It's triggered frequently, so use sparingly.
import { Component, AfterContentChecked } from '@angular/core';
@Component({
selector: 'app-content-checked-parent',
template: `<ng-content></ng-content>`
})
export class ContentCheckedParentComponent implements AfterContentChecked {
ngAfterContentChecked(): void {
console.log('ngAfterContentChecked: Projected content checked.');
}
}
ngAfterViewInit
Called once after Angular initializes the component's view and its child views. This hook is ideal for direct DOM manipulation, working with child components (using @ViewChild or @ViewChildren), or integrating third-party libraries that require access to the rendered view. It is important to note that content children are not available here, only view children.
import { Component, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
import { ChildComponent } from './child.component'; // Assuming you have a child component
@Component({
selector: 'app-view-init-component',
template: `
<p #myParagraph>This is a paragraph.</p>
<app-child-component></app-child-component>
`
})
export class ViewInitComponent implements AfterViewInit {
@ViewChild('myParagraph') paragraphElement: ElementRef;
@ViewChild(ChildComponent) childComponent: ChildComponent;
ngAfterViewInit(): void {
console.log('ngAfterViewInit: Component view and child views initialized.');
console.log('Paragraph text:', this.paragraphElement.nativeElement.textContent);
// this.childComponent.someMethod(); // Uncomment if ChildComponent has such a method
}
}
ngAfterViewChecked
Called after every ngDoCheck and after the component's view and child views have been checked. Similar to ngAfterContentChecked, but specifically for the component's own view and its children. It's suitable for performing actions that rely on the updated state of the view after every change detection cycle. Use with extreme care to avoid performance issues or infinite loops if you modify the view within this hook.
import { Component, AfterViewChecked } from '@angular/core';
@Component({
selector: 'app-view-checked-component',
template: `<p>View checked.</p>`
})
export class ViewCheckedComponent implements AfterViewChecked {
ngAfterViewChecked(): void {
console.log('ngAfterViewChecked: Component view and child views checked.');
}
}
ngOnDestroy
Called just before Angular destroys the component. This hook is crucial for cleanup logic, such as unsubscribing from observables, detaching event handlers, or clearing intervals/timers, to prevent memory leaks and ensure resources are properly released. It's guaranteed to be called once when the component is removed from the DOM.
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription, interval } from 'rxjs';
@Component({
selector: 'app-destroy-component',
template: '<p>Destroy me!</p>'
})
export class DestroyComponent implements OnInit, OnDestroy {
private intervalSubscription: Subscription;
ngOnInit() {
this.intervalSubscription = interval(1000).subscribe(val => console.log(val));
}
ngOnDestroy(): void {
console.log('ngOnDestroy: Component is being destroyed. Cleaning up resources.');
this.intervalSubscription.unsubscribe(); // Prevent memory leak
}
}