What Angular Lifecycle hooks are
Angular lifecycle hooks are special methods that allow you to tap into specific stages of a component’s or directive’s lifecycle. By implementing these hooks, you can execute custom code at specific points in the component’s lifecycle, giving you more control over its behaviour and performance.
NOTE: Remember? I told you that the For-Each Loop is the cool sister of the loop statements. I'll tell you about them more when the time comes but just keep in mind that usually, if you can, it's best to use this one over the others.
Angular lifecycle hooks are methods that Angular’s change detection mechanism invokes at specific points during a component’s or directive’s lifecycle. When you implement these hooks in your component or directive class, you provide Angular with a way to execute custom code at these specific points.
Angular implements lifecycle hooks as TypeScript interfaces with method signatures. When you implement a lifecycle hook in your component, you import the corresponding interface from the ‘@angular/core’ module and add the method to your component class. This allows Angular to identify and call the hook methods at the right time.
lifecycle hooks walk-through
There are 8 most common Angular lifecycle hooks: ngOnChanges(), ngOnInit(), ngDoCheck(), ngAfterContentInit(), ngAfterContentChecked(), ngAfterViewInit(), ngAfterViewChecked(), ngOnDestroy().
Let’s walk through all of them as they are pretty important if you’re serious about this whole becoming Angular Developer thing. I’m gonna talk about them in the order they usually happen in the app.
ngOnChanges() – the Input Inspector
The ngOnChanges() method is like Inspector Gadget keeping an eye on input properties and alerting the component of any changes. It is called whenever Angular detects changes in input properties. Any changes. It receives an object containing current and previous values of the changed properties.It executes before ngOnInit() for the first time and then, whenever one or more input properties change.
Example code implementation:
ngOnChanges(changes: SimpleChanges): void {
for (const propName in changes) {
const change = changes[propName];
console.log(`Property '${propName}' changed from '${change.previousValue}' to '${change.currentValue}'`);
}
}
In this example, ngOnChanges() iterates through the changed input properties in the ‘changes’ object and logs the property name, previous value, and current value to the console.
Examples of use:
- Updating component state based on changed input properties.
- Triggering side effects, such as fetching data from a server.
- Applying custom validation or data manipulation based on input property changes.
- Updating visual elements or styles in response to input property changes.
Best practice:
- Avoid complex logic or performance-heavy tasks, as ngOnChanges can be called frequently.
- Don’t use it for initialisation tasks that only need to be executed once.
- Ensure to import
SimpleChangesfrom ‘@angular/core’.
ngOnInit() – the Genesis Guru
The ngOnInit() method is an expert at handling the beginning of a component’s life. It is called just once, right after the first execution of the ngOnChanges(). It is used for one-time initialisation tasks, such as fetching data or setting up event listeners.
Example code implementation:
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css']
})
export class MyComponent implements OnInit {
@Input() eventSource: string;
constructor(private eventService: EventService) {}
ngOnInit(): void {
if (this.eventSource) {
this.eventService.on(`${this.eventSource}:customEvent`, this.handleCustomEvent.bind(this));
}
}
(...)
}
In this example, the component has an input property called eventSource. The ngOnInit() method sets up an event listener based on the value of the eventSource property. If eventSource is defined, the event listener is registered for an event named <eventSource>:customEvent.
Example of use:
- Fetching data
- Initializing component state
- Subscribing to observables
- Setting up event listeners
- Initializing form controls
- Calculating derived values
Best practice:
- Don’t use for tasks that depend on the component’s view or child views, as they might not be initialised yet.
- Use for setting up event listeners that only need to be created once.
- Avoid duplicating initialisation tasks in both ngOnChanges and ngOnInit.
ngDoCheck() – the Revision Ranger
The ngDoCheck() method diligently patrols the component’s territory, ensuring no change goes undetected. It is called during every change detection run, immediately after ngOnChanges() and ngOnInit(). It is often used to implement custom change detection strategies especially when Angular’s default change detection doesn’t cover your requirements.
Example code implementation:
import { Component, Input, DoCheck } from '@angular/core';
@Component({
selector: 'app-my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.css']
})
export class MyComponent implements DoCheck {
@Input() complexInput: any;
private previousValue: any;
constructor() {
this.previousValue = JSON.stringify(this.complexInput);
}
ngDoCheck(): void {
const currentValue = JSON.stringify(this.complexInput);
if (this.previousValue !== currentValue) {
console.log('complexInput has changed');
this.previousValue = currentValue;
}
}
}
In this example, we have a component with a complex input property called complexInput. Angular’s default change detection might not detect changes in this property if its properties are modified without changing the reference itself. To handle this scenario, we implement the DoCheck interface and provide a custom change detection strategy in the ngDoCheck() method.
Best practice:
- Watch out for performance issues, as ngDoCheck is called frequently.
- Use it to monitoring changes in complex objects or objects not supported by Angular’s default change detection.
- Balance custom change detection logic with performance considerations.
ngAfterContentInit() – the Content Copilot
The ngAfterContentInit() method helps guide your component’s journey through the content that is projected into it, just like a copilot helps guide an airplane through its journey in the sky.
It is only called once, after Angular projects external content into the component’s view. This includes any child components that are projected into the component using ng-content tags. This hook is useful for performing any initialization logic that depends on the content projected into the component. For example, if you have a component that expects certain content to be projected into it and you need to access that content (f.e. using @ViewChild or @ContentChild decorators) in order to perform some initialization, you can use ngAfterContentInit() to do so.
Example code implementation:
@ContentChild('someElement') someElement: ElementRef;
ngAfterContentInit(): void {
// Access 'someElement' here
}
Best practice:
- Avoid making assumptions about the state of other components’ views, as they might not be initialised yet.
ngAfterContentChecked() – the Content Therapist:
The ngAfterContentChecked() method helps the projected content work through any issues or problems it may be facing (hey, let him who never needed a shoulder to cry on be the first to throw a stone 😉 ). It is called after Angular checks the projected content, both after the initial ngAfterContentInit() and subsequent checks.
Example code implementation:
ngAfterContentChecked(): void {
// Respond to changes in projected content here
}
Best practice:
- Avoid performance-heavy tasks, as ngAfterContentChecked() is called frequently.
ngAfterViewInit() – the View Viking:
The ngAfterViewInit() method fearlessly charges into battle to tame the view. This hook is called only once after the view and child views have been initialized, and it is useful for performing any initialization logic that depends on the component’s view being ready.
Note that ngAfterViewInit() is called after ngAfterContentInit(), which is called after the component’s content has been projected into it using ng-content. If you need to perform any initialization logic that depends on the content projected into the component, you should use ngAfterContentInit() instead.
Example code implementation:
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
@Component({
selector: 'my-component',
template: `
<h1 #myHeading>Hello World!</h1>
`
})
export class MyComponent implements AfterViewInit {
@ViewChild('myHeading') myHeading: ElementRef;
ngAfterViewInit() {
// Do any initialization logic that depends on the view being ready
console.log(this.myHeading.nativeElement.textContent);
}
}
In this example, MyComponent uses the @ViewChild decorator to obtain a reference to the h1 element with the local variable #myHeading that is defined in the template. The ngAfterViewInit() is then used to access the textContent property of the h1 element.
Best practice:
- Avoid modifying data-bound properties, as it may result in an ExpressionChangedAfterItHasBeenCheckedError.
- Remember to import ViewChild or ViewChildren from ‘@angular/core’ when using them.
ngAfterViewChecked() – the View Librarian:
The ngAfterViewCVhecked() method is like a true librarian as it ensures that all elements in the view are properly organized and cataloged. It is called after Angular checks the component’s view and child views, both after the initial ngAfterViewInit() and subsequent checks.
Since it’s executed after every change detection cycle (after the view and child views have been checked for changes) it is called frequently. It is useful for performing any logic that needs to be executed every time the view is checked for changes.
Example code implementation:
import { Component, ViewChild, ElementRef, AfterViewChecked } from '@angular/core';
@Component({
selector: 'my-component',
template: `
<h1 #myHeading>Hello World!</h1>
`
})
export class MyComponent implements AfterViewChecked {
@ViewChild('myHeading') myHeading: ElementRef;
ngAfterViewChecked() {
// any logic to be executed whenever the view is checked for changes
console.log('The view has been checked', this.myHeading.nativeElement.textContent);
}
}
In this example, MyComponent uses the @ViewChild decorator to obtain a reference to the h1 element with the local variable #myHeading that is defined in the template. ngAfterViewChecked() is then used to access the textContent property of the h1 element.
Best practice:
- Be cautious not to introduce performance issues, as ngAfterViewChecked is called frequently.
ngOnDestroy() – the Destruction Dynamo:
The ngOnDestroy() is a force to be reckoned with when it comes to tearing down components and services. It is called only once, just before the component or directive is removed from the DOM, and it is useful for performing any cleanup or finalization tasks such as unsubscribing from observables or removing event listeners.
Example code implementation:
import { Component, OnDestroy } from '@angular/core';
@Component({
selector: 'my-component',
template: `
<p>Hello World!</p>
`
})
export class MyComponent implements OnDestroy {
ngOnDestroy() {
// Do any cleanup or finalization tasks here
console.log('The component has been destroyed');
}
}
In this example, MyComponent implements the OnDestroy interface and defines the ngOnDestroy() method. ngOnDestroy() is then used to perform any cleanup or finalization tasks, such as closing open connections or unsubscribing from observables. Whenever the component is destroyed, Angular will call ngOnDestroy() and log a message to the console indicating that the component has been destroyed.
Best practice:
- It’s important to clean up any subscriptions, event listeners, or other resources in ngOnDestroy() to prevent memory leaks in your Angular application.
So there you have it, folks! Angular lifecycle hooks are like a toolbox for controlling the behavior and performance of your components. From the input inspector to the destruction dynamo, each hook serves a unique purpose in the component lifecycle. By using these hooks wisely, you can become a true lifecycle Jedi and harness the power of the Angular force. May the hooks be with you!
/*
Until next time,
Stay hooked till the end (like Angular's ngOnDestroy()) and keep coding!
eMs
*/
