Angular Project with Lifecycle Hooks.

Angular Project with Lifecycle Hooks.

Angular component has a lifecycle hooks. It goes through different phases in its life from begin to the end.

We'll dive in two steps:

  • Create new Angular Project.
  • Understand each phase of Lifecycle Hooks.

📥 Download Source Code: Clone this GitHub Repository.


Create new Angular Project.

#1 To create new Project run this command:

ng new project-name

#2 Open project folder in VS Code. (Install → VS Code)


#3 Run npm install to make sure all dependencies are installed.
(this will create new node_modules folder in the project directory)


#4.1 To create new Class run this command:

ng generate class class-name

I run ng generate class Comments to generate a class, this will create a new class file at src/app/comments.ts

#4.2 Update Class:
Open up your src/app/comments.ts file and update it as follows:

import { Injectable } from "@angular/core";

@Injectable()
export class Comments {
  public title: string;
  public description: string;
  public hide: boolean;

  constructor(title: string, description: string) {
    this.title = title;
    this.description = description;
    this.hide = true;
  }

  toggle() {
    this.hide = !this.hide;
  }
}

#5.1 Again create new Component run this command:

ng generate component component-name

I run ng generate component Comments to generate a component, this will create 4 new files at src/app/comments

#5.2 Update Component:
Open up your src/app/comments/comments.component.ts file and update it as follows:

import { Component, OnInit, Input } from '@angular/core';
import { Comments } from '../comments';

@Component({
  selector: 'app-comments',
  templateUrl: './comments.component.html',
  styleUrls: ['./comments.component.css'],
})
export class CommentsComponent implements OnInit {

  @Input('comment') data: Comments;

  // Adding Hooks...

  constructor() {
    console.log(`constructor, new - data is ${this.data}`);
  }

  ngOnChanges() {
    console.log(`ngOnChanges - data is ${this.data}`);
  }

  ngOnInit() {
    console.log(`ngOnInit  - data is ${this.data}`);
  }

  ngDoCheck() {
    console.log("ngDoCheck")
  }

  ngAfterContentInit() {
    console.log("ngAfterContentInit");
  }

  ngAfterContentChecked() {
    console.log("ngAfterContentChecked");
  }

  ngAfterViewInit() {
    console.log("ngAfterViewInit");
  }

  ngAfterViewChecked() {
    console.log("ngAfterViewChecked");
  }

  ngOnDestroy() {
    console.log("ngOnDestroy");
  }

}

#5.3 Update Component Template:
Open up your src/app/comments/comments.component.html file and update it as follows:

<div class="card card-block">
  <h4 class="card-title">
    <ng-content select=".title"></ng-content>
  </h4>
  <p class="card-text"
     [hidden]="data.hide">
    <ng-content select=".description"></ng-content>
  </p>
  <button class="btn btn-primary"
     (click)="data.toggle()">Toggle Description
  </button>
</div>

#6.1 Again create new Component run this command:

ng generate component component-name

I run ng generate component CommentsList to generate a component, this will create 4 new files at src/app/comments-list

#6.2 Update Component:
Open up your src/app/comments-list/comments-list.component.ts file and update it as follows:

import { Component, OnInit } from '@angular/core';
import { Comments } from '../comments';

@Component({
  selector: 'app-comments-list',
  templateUrl: './comments-list.component.html',
  styleUrls: ['./comments-list.component.css']
})
export class CommentsListComponent implements OnInit {

  comments: Comments[];

  constructor() {
    this.comments = [
      new Comments('Comment Title-1', 'So the ngOnInit() method will be called after each initialization of the component or only the first time ?'),

      new Comments('Comment Title-2', 'You said that constructor mainly initialize fields member but you said that you can initialize some fields in the ngOnInit too, what the difference between these two initializations ?'),
    ];
  }

  ngOnInit() {
  }

  addComment() {
    this.comments.unshift(
      new Comments('Comment New Title', 'Description of the new Comment comes here...'));
  }

  clearAllComments() {
    this.comments = [];
  }

}

#6.3 Update Component Template:
Open up your src/app/comments/comments-list.component.html file and update it as follows:

<app-comments *ngFor="let c of comments" [comment]="c">
  <span class="title">{{ c.title }}</span>
  <h3 class="description">{{ c.description }}</h3>
</app-comments>

<br>
<hr>

<p>
  <button type="button"
          class="btn btn-success"
          (click)="addComment()">Add Comment
  </button>
  <button type="button"
          class="btn btn-danger"
          (click)="clearAllComments()">Clear All Comments
  </button>
</p>

#7 Update App Component:
Open up your src/app/app.component.ts file and update it as follows:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `<app-comments-list></app-comments-list>`,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'Angular Component Lifecycle Hook App';
}

#8 Update App Module:
Finally, Open up your src/app/app.module.ts file and update it as follows:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import { CommentsListComponent } from './comments-list/comments-list.component';
import { CommentsComponent } from './comments/comments.component';

@NgModule({
  declarations: [
    AppComponent,
    CommentsListComponent,
    CommentsComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

We successfully created an Angular App. Let's run this app..

To run Angular app, run this command:

ng serve

** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **

Feel free to change URL - If your app runs on different port. Like this http://localhost:YOUR-PORT-NO/


Let's practically understand Lifecycle Hooks.

If we click "Add Comment" then a Comment is added to the list and Angular creates an instance of the CommentsComponent and that will trigger the lifecycle hooks.. Look below - an image after "Add Comment" is clicked.

Looking at the console at this time we can see these logs:

constructor, new - data is undefined
ngOnChanges - data is [object Object]
ngOnInit  - data is [object Object]
ngDoCheck
ngAfterContentInit
ngAfterContentChecked
ngAfterViewInit
ngAfterViewChecked
  • We can clearly see that by the time constructor is called the input property is undefined.
  • However by the time the ngOnChanges hook is called the input property is now set to the Comment object.

Important

The best place to initialise your 'components' is in the ngOnInit lifecycle hook and not the constructor because at this point only the input property bindings are being processed.

As we see here ngOnChanges called before ngOnInit.
Although the reason to use ngOnInit and not ngOnChanges to initialise a component is that ngOnInit is only called once whereas ngOnChanges is called every time the input properties change.

Next...

When we press the "Clear All Comments" button, Angular deletes the CommentsComponent and calls the ngOnDestroy hook which we can see in the logs below:

ngOnDestroy calls for 3 times because there were 3 comments.

Understand each phase of Lifecycle Hooks.

The hooks of the Component are executed in the following order ↙

↓  constructor
↓  ngOnChanges
↓  ngOnInit
↓  ngDoCheck
↓  ngAfterContentInit
↓  ngAfterContentChecked
↓  ngAfterViewInit
↓  ngAfterViewChecked
↓  ngOnDestroy

Let's dive into each phase..

↓  constructor
This is invoked when Angular creates a component or directive by calling new keyword on the class.
example: new Comments("do..do..", "do..do..do..do..");

↓  ngOnChanges
Invoked every time there is a change in one of the input properties/variables of the component.

↓  ngOnInit
Invoked when component has been initialized (This hook is only called once after the first ngOnChanges)

↓  ngDoCheck
Invoked when the change detector of the given component is invoked. It allows us to implement our own change detection algorithm for the component.


Important to know: `ngDoCheck` and `ngOnChanges` should not be implemented together on the same component because when we implement our own change detection algorithm in `ngDoCheck` we don't need of `ngOnChanges`.

↓  ngAfterContentInit
Invoked after Angular performs any 'content projection' into the components view.


'Content projection': If we add the tag `< ng-content > ` anywhere in our HTML template of component. The inner content of the component associated with the selector tag will be projected into this space.

↓  ngAfterContentChecked
Invoked each time the content of the given component has been checked by the change detection mechanism of Angular.

↓  ngAfterViewInit
Invoked when the component’s view has been fully initialized.

↓  ngAfterViewChecked
Invoked each time the view of the given component has been checked by the change detection mechanism of Angular.

↓  ngOnDestroy
This method will be invoked just before Angular destroys the component. Use this hook to unsubscribe observables and detach event handlers to avoid memory leaks.


Done! 🤩 It’s that simple to understand 'The Lifecycle Hooks in Angular'.

See you later 👋👋 bye.. bye..


Feel free to comment down in the comment box… If I missed anything or anything is incorrect or anything does not works for you :)
Stay connected for more articles.