Angular

Angular Constructor and Lifecycle Hooks

Angular's lifecycle hooks and constructor, including a timeline of their sequence and annotations explaining their purpose and use cases

Angular provides a robust component lifecycle mechanism that allows developers to hook into specific moments during a component’s existence. Each lifecycle hook has a distinct purpose and specific timing during the lifecycle of a component or directive.

In this article, we’ll cover Angular’s constructor and all lifecycle hooks, describing when they are called, how they work, and best practices for using them effectively.


Overview of Angular Constructor and Lifecycle Hooks

What is the Constructor?

The constructor is a TypeScript feature used to initialize a class and inject dependencies. It is the first method called when an instance of the component or directive is created.

What are Lifecycle Hooks?

Lifecycle hooks are Angular-provided methods that allow developers to insert logic at various stages of a component’s lifecycle, from initialization to destruction.


Angular Constructor

When is it Called?

  • The constructor is called when Angular creates a new instance of the component or directive class.

Purpose

  • To initialize class members.
  • To inject dependencies via Angular’s Dependency Injection system.

Best Practices

  1. Keep it Simple: Avoid putting logic in the constructor that depends on bindings or Angular’s lifecycle. Use lifecycle hooks for that.
  2. Dependency Injection Only: Use the constructor to inject services or initialize basic class properties.

Example

typescriptCopy codeimport { Component } from '@angular/core';
@Component({
  selector: 'app-example',
  template: `<p>Example works!</p>`,
})
export class ExampleComponent {
  constructor() {
    console.log('Constructor called');
  }
}

Angular Lifecycle Hooks

Angular’s lifecycle hooks are called at specific stages in the component or directive lifecycle.

1. ngOnChanges

When is it Called?

  • Every time an input property (@Input) value changes.
  • Called before ngOnInit.

Purpose

  • To react to changes in input properties and adjust internal logic or state.

Use Cases

  1. Validating or transforming new input values.
  2. Updating derived data based on changes.

Example

typescriptCopy codeimport { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
@Component({
  selector: 'app-onchanges',
  template: `<p>{{ message }}</p>`,
})
export class OnChangesComponent implements OnChanges {
  @Input() message!: string;
  ngOnChanges(changes: SimpleChanges): void {
    console.log('Changes:', changes);
  }
}

2. ngOnInit

When is it Called?

  • Once, after the component is initialized and input properties are set.

Purpose

  • To perform component initialization, such as fetching data or setting up properties that depend on bindings.

Use Cases

  1. Fetching data from a service.
  2. Initializing complex logic or properties.

Example

typescriptCopy code@Component({
  selector: 'app-oninit',
  template: `<p>{{ data }}</p>`,
})
export class OnInitComponent implements OnInit {
  data!: string;
  ngOnInit(): void {
    this.data = 'Initialized data';
    console.log('ngOnInit called');
  }
}

3. ngDoCheck

When is it Called?

  • During every change detection cycle.

Purpose

  • To implement custom change detection logic.

Use Cases

  1. Detecting changes Angular doesn’t track by default.
  2. Monitoring specific state changes.

Example

typescriptCopy code@Component({
  selector: 'app-docheck',
  template: `<p>{{ count }}</p>`,
})
export class DoCheckComponent implements DoCheck {
  count = 0;
  ngDoCheck(): void {
    console.log('ngDoCheck called');
    this.count++;
  }
}

4. ngAfterContentInit

When is it Called?

  • Once, after Angular projects external content into the component.

Purpose

  • To perform logic related to <ng-content> projected content.

Use Cases

  1. Interacting with projected content.
  2. Initializing logic that depends on the presence of <ng-content>.

Example

typescriptCopy code@Component({
  selector: 'app-after-content-init',
  template: `<ng-content></ng-content>`,
})
export class AfterContentInitComponent implements AfterContentInit {
  ngAfterContentInit(): void {
    console.log('ngAfterContentInit called');
  }
}

5. ngAfterContentChecked

When is it Called?

  • After every change detection cycle involving projected content.

Purpose

  • To handle updates to projected content dynamically.

Use Cases

  1. Validating or responding to changes in <ng-content>.

Example

typescriptCopy code@Component({
  selector: 'app-after-content-checked',
  template: `<ng-content></ng-content>`,
})
export class AfterContentCheckedComponent implements AfterContentChecked {
  ngAfterContentChecked(): void {
    console.log('ngAfterContentChecked called');
  }
}

6. ngAfterViewInit

When is it Called?

  • Once, after Angular initializes the component’s view and child views.

Purpose

  • To perform logic that depends on the DOM being fully rendered.

Use Cases

  1. Accessing and modifying DOM elements.
  2. Initializing libraries dependent on DOM presence.

Example

typescriptCopy code@Component({
  selector: 'app-after-view-init',
  template: `<p #viewContent>View initialized</p>`,
})
export class AfterViewInitComponent implements AfterViewInit {
  @ViewChild('viewContent') viewContent!: ElementRef;
  ngAfterViewInit(): void {
    console.log('ngAfterViewInit called:', this.viewContent.nativeElement.textContent);
  }
}

7. ngAfterViewChecked

When is it Called?

  • After every change detection cycle involving the component’s view or child views.

Purpose

  • To respond to updates in the view.

Use Cases

  1. Validating or modifying view-related logic.

Example

typescriptCopy code@Component({
  selector: 'app-after-view-checked',
  template: `<p>View checked</p>`,
})
export class AfterViewCheckedComponent implements AfterViewChecked {
  ngAfterViewChecked(): void {
    console.log('ngAfterViewChecked called');
  }
}

8. ngOnDestroy

When is it Called?

  • Just before the component is destroyed.

Purpose

  • To clean up resources such as subscriptions, event listeners, or timers.

Use Cases

  1. Unsubscribing from Observables.
  2. Removing event listeners or intervals.

Example

typescriptCopy code@Component({
  selector: 'app-ondestroy',
  template: `<p>Component being destroyed</p>`,
})
export class OnDestroyComponent implements OnDestroy {
  ngOnDestroy(): void {
    console.log('ngOnDestroy called');
  }
}

Lifecycle Hook Sequence

Here’s the order in which lifecycle hooks are called:

  1. Constructor
  2. ngOnChanges
  3. ngOnInit
  4. ngDoCheck
  5. ngAfterContentInit
  6. ngAfterContentChecked
  7. ngAfterViewInit
  8. ngAfterViewChecked
  9. ngOnDestroy

Best Practices for Lifecycle Hooks

  1. Separate Responsibilities
    Use each hook for its intended purpose to keep your code clean and maintainable.
  2. Avoid Heavy Logic
    Do not perform heavy computations in frequently called hooks like ngDoCheck, ngAfterContentChecked, or ngAfterViewChecked.
  3. Use Cleanup in ngOnDestroy
    Always clean up resources like subscriptions, intervals, and event listeners to prevent memory leaks.
  4. Test Lifecycle Dependencies
    Test your components to ensure that lifecycle-dependent logic works as expected under various conditions.

Conclusion

Angular’s lifecycle hooks provide developers with a comprehensive way to manage the lifecycle of components and directives. By understanding when these hooks are triggered and using them appropriately, you can build efficient, maintainable, and robust Angular applications.

Leave a Reply

Your email address will not be published. Required fields are marked *