Understanding Angular Life Cycle Hooks

Understanding Angular Life Cycle Hooks

Understanding Angular Life Cycle Hooks

In an Angular application, the components have their whole lifecycle managed by Angular, from creation to destruction. And it provides us access to lifecycle hooks, which allows us to act in key moments during the component’s lifecycle.

In order to use those hooks, we have to tell Angular we want to implement the desired hook interface. Angular inspects component classes and calls the hook methods if they are defined. Below is how this syntax goes (for OnInit):

export class MyComponent implements OnInit {

    constructor() { }
    ngOnInit() {
        // Insert Logic Here!
    }
}

This is the list of the provided hooks by Angular, which are called in this exact order and we will dig into each one of them right after:

╔════════════════════════════╗
║   lifecycle hooks          ║
╠════════════════════════════╣
║   ngOnChanges()            ║
╠════════════════════════════╣
║   ngOnInit()               ║
╠════════════════════════════╣
║   ngDoCheck()              ║
╠════════════════════════════╣
║   ngAfterContentInit()     ║ 
╠════════════════════════════╣
║   ngAfterContentChecked()  ║  
╠════════════════════════════╣
║   ngAfterViewInit()        ║
╠════════════════════════════╣
║   ngAfterViewChecked()     ║
╠════════════════════════════╣
║   ngOnDestroy()            ║
╚════════════════════════════╝

ngOnChanges

This method is called once on component’s creation and then every time changes are detected in one of the component’s input properties. It receives a SimpleChanges object as a parameter, which contains information regarding which of the input properties has changed - in case we have more than one - and its current and previous values.

export class MyComponent implements OnChanges {

    ngOnChanges(changes: SimpleChanges) {
        // Insert Logic Here!
    }
}

Obs: when a value has not been set for an input property, its value is set by default as the string “CD_INIT_VALUE”.

This is one of the lifecycle hooks which can come in handy in multiple use cases. It is very useful if you need to handle any specific logic in the component based on the received input property.

Let’s say you have a component that displays users information and that it receives that UserInfo object as an input parameter. Below is an example on how you could use ngOnChanges in that component to add logic to handle changes on the UserInfo property.

export class UserInfoComponent implements OnChanges {

  @Input userInfo: UserInfo;
  ngOnChanges(changes: SimpleChanges) {
    const previousValue = changes['userInfo'].previousValue;
    const currentValue = changes['userInfo'].currentValue;
    // Your Logic!
  }
}

ngOnInit

This method is called only once during the component lifecycle, after the first ngOnChanges callAt this point, within this method, you can have access not only to data-bound properties but also the component’s input properties.

export class MyComponent implements OnInit {

  ngOnInit() {
      // Insert Logic Here!
  }
}

This is one of the most used lifecycle hooks in Angular. Here is where you might set requests to the server to load content, maybe create a FormGroup for a form to be handled by that component, set subscriptions and much more. Basically, it is where you can perform any initializations shortly after the component’s construction.

So let’s say you have a register form component and you’d like to create the form based on fields received from the server that are fetched according to an user type. Below is an example on how you could use ngOnInit to achieve that.

export class RegisterFormComponent implements OnInit {

  public formGroup: FormGroup;
  private _userType: UserTypeEnum;
  constructor(
    private _activatedRoute: ActivatedRoute,
    private _myService: MyService
  ) {
    this._userType =
      this._activatedRoute.snapshot.paramMap.get('userType');
  }
  ngOnInit() {
    this._myService.getFormFieldsByType(
      this._userType
    ).subscribe((response) => {
      this.formGroup = this._createForm(
        response.data
      );
    }, (error) => console.error( error ) );
  }
  private _createForm(formFields: Array<FormFields>): FormGroup {
    // FormGroup Creation Logic!
  }
}

Obs: Constructor vs NgOnInit ?

Probably you have wondered why place your initialization logic inside ngOnInit when you can do it in the class constructor. Well, basically the constructor is best left to be used for dependency injection and our initialization logic should be put on ngOnInit.

That is because the Javascript engine is what handles the constructor, not Angular. And this is one of the reasons why the ngOnInit hook was created, which is called by Angular and becomes part of the component’s lifecycle that is managed by it. Also, of course, due to the fact that you can’t yet access component’s Input properties on the constructor.

ngDoCheck

This hook can be interpreted as an “extension” of ngOnChanges. You can use this method to detect changes that Angular can’t or won’t detect. It is called in every change detection, immediately after the ngOnChanges and ngOnInit hooks.

export class MyComponent implements DoCheck {

  ...
  private _currentValue;
  private _previousValue;
  public changeDetected: boolean = false;
  ...
  ngDoCheck() {
    if (this._previousValue !== this._currentValue) {
      this.changeDetected = true;
      // Insert Logic Here
    }
  }
}

Obs: this hook is really costly, since it is called with enormous frequency; after every change detection cycle no matter where the change occurred. Therefore, its usage should be careful in order to not affect the user experience.


AfterContent & AfterView


Before we talk about these hooks, first we need to understand what do they relate to:

“The AfterContent hooks concern ContentChildren, the child components that Angular projected into the component.
The AfterView hooks concern ViewChildren, the child components whose element tags appear within the component's template.”

In order to illustrate what we are talking about, suppose we have the component below, which have both ContentChild and ViewChild. We’d have a ng-content tag, which will render content passed from the parent, and a reference to the div ViewChild (which we called wrapper).

@Component({
  selector: 'my-component',
  template: `
    <div #wrapper >
      <ng-content></ng-content>
    </div>`
})
export class MyComponent implements {
  @ViewChild('wrapper') wrapper: ElementRef;
  @ContentChild('content') content: ElementRef;

}

ngAfterContentInit


This method is called only once during the component’s lifecycle, after the first ngDoCheck. Within this hook, we have access for the first time to the ElementRef of the ContentChild after the component’s creation; after Angular has already projected the external content into the component’s view.

@Component({
  selector: 'my-component',
  template: `
    <div>
      <ng-content></ng-content>
    </div>`
})
export class MyComponent implements AfterContentInit {
  @ContentChild('content') content: ElementRef;

  ngAfterContentInit() {
    // Now we have access to 'this.content'!
    // Insert Logic Here!
  }
}

ngAfterContentChecked


This method is called once during the component’s lifecycle after ngAfterContentInit and then after every subsequent ngDoCheck. It is called after Angular has already checked the content projected into the component in the current digest loop.

@Component({
  selector: 'my-component',
  template: `
    <div>
      <ng-content></ng-content>
    </div>`
})
export class MyComponent implements AfterContentChecked {
  @ContentChild('content') content: ElementRef;

  ngAfterContentChecked() {
    // We have access to 'this.content'!
    // Content has already been checked!
    // Insert Logic Here!
  }
}

ngAfterViewInit


This method is called only once during the component’s lifecycle, after ngAfterContentChecked. Within this hook, we have access for the first time to the ElementRefs of the ViewChildren after the component’s creation; after Angular has already composed the component’s views and its child views.

@Component({
  selector: 'my-component',
  template: `
    <div #wrapper >
      ...
    </div>`
})
export class MyComponent implements AfterViewInit {
  @ViewChild('wrapper') wrapper: ElementRef;

  ngAfterViewInit() {
    // Now we have access to 'this.wrapper'
    // Insert Logic Here!
  }

}

This hook is quite useful when you need to load content on your view that depends on its view’s components; for instance when you need to set a video player or create a chart from a canvas element. Below is an example on how you could set a chart using the ngAfterViewInit hook.

@Component({
  selector: 'my-component',
  template: `
    <div>
      <canvas id="myCanvas" ></canvas>
    </div>`
})
export class MyComponent implements AfterViewInit {
  ngAfterViewInit() {
    // Now we can get the canvas element by its id
    // in order to create the chart
    this.chart = new Chart('radarCanvas', {
      ...
    });
  }
}

ngAfterViewChecked


This method is called once after ngAfterViewInit and then after every subsequent ngAfterContentChecked. It is called after Angular has already checked the component’s views and its child views in the current digest loop.

@Component({
  selector: 'my-component',
  template: `
    <div #wrapper >
      ...
    </div>`
})
export class MyComponent implements AfterViewChecked {
  @ViewChild('wrapper') wrapper: ElementRef;

  ngAfterViewChecked() {
    // Now we have access to 'this.wrapper'!
    // View has already been checked!
    // Insert Logic Here!
  }
}


ngOnDestroy


Lastly, this method is called only once during the component’s lifecycle, right before Angular destroy itHere is where you should inform the rest of your application that the component is being destroyed, in case there are any actions to be done regarding that information.

export class MyComponent implements OnDestroy {

  ngOnDestroy() {
      // Insert Logic Here!
  }
}

Also it is where you should put all your cleanup logic for that component. For instance, it is where you can remove any localstorage information and, most importantly, unsubscribe observables/detach event handlers/stop timers, etc. to avoid memory leaks.

export class MyComponent implements OnDestroy {

  private _mySubject: Subject<string> = new Subject();
  ...
  ngOnDestroy() {
    localStorage.removeItem('storageKey');
    this._searchSubject.unsubscribe();
  }
}

Obs: ngOnDestroy is not called when the user refreshes the page or closes the browser. So, in case you need to handle some cleanup logic on those occasions as well, you can use the HostListener descorator, as shown below:

  @HostListener(‘window:beforeunload’)
  ngOnDestroy() {
     // Insert Logic Here!
  }

Understanding Angular Lifecyle Hooks, their objectives and when they are called can be very useful when creating Angular applications. Therefore it is important to know how they work and what you can achieve with them in order to be able to apply it whenever you might need it.

Hope it helps! 😉



Share Article:
  • Facebook
  • Instagram
  • LinkedIn
  • Twitter
  • Recent Posts