Skip to content

Using ngOnChanges

ngOnChanges Cover

ngOnChanges is a lifecycle hook that is being invoked every time a variable decorated by @Input changes. We are intercepting and handling changes inside a ngOnChanges(changes: SimpleChanges) method. We are using the SimpleChanges API, an interface that represents all the changes stored by a particular property.

ngOnChanges actually triggers even before ngOnInit hook, because Angular considers the very first initialization of the properties to be a change in itself.

onChanges example

Let us work on a simple example. We have the user-card.component.html below:

<div class="wrapper">
  <div class="basic-info">
    <div class="image">
      <img
        src="https://avatars.dicebear.com/api/male/tom.svg?background=%230000ff"
      />
    </div>
    <div #personalData class="personal-data">
      <h3>Name:</h3>
      <p>{{ name }}</p>
      <h3>Surname:</h3>
      <p>Doe</p>
    </div>
  </div>
  <form [formGroup]="userForm" (submit)="submitNewName($event)">
    <label for="name">Enter your name:</label>
    <input
      formControlName="name"
      type="text"
      id="name"
      placeholder="{{ name }}"
    />
    <button type="submit">Submit</button>
  </form>

  <app-user-list [name]="name"></app-user-list>
</div>

And the logic here (user-card.component.ts):

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-user-card',
  templateUrl: './user-card.component.html',
  styleUrls: ['./user-card.component.css'],
})
export class UserCardComponent implements OnInit {
  name = 'Tom';

  userForm = new FormGroup({
    name: new FormControl('', [Validators.required]),
  });

  constructor() {}

  ngOnInit() {}

  submitNewName(event: any) {
    console.log(this.userForm);
  }
}

We created a simple form with a single property of name, and a child component UserList to which we provide the name as a property. You can see the user-list.component.html and user-list.component.ts files below:

<div class="names">
  <div class="name" *ngFor="let name of names">
    {{ name }}
  </div>
</div>
import {
  Component,
  Input,
  OnChanges,
  OnInit,
  SimpleChanges,
} from '@angular/core';

@Component({
  selector: 'app-user-list',
  templateUrl: './user-list.component.html',
  styleUrls: ['./user-list.component.css'],
})
export class UserListComponent implements OnInit, OnChanges {
  @Input() name: string;
  names = [];

  constructor() {}

  ngOnInit() {}

  ngOnChanges(changes: SimpleChanges) {
    console.log(changes);
  }
}

With the changes above applied this is our starting application:

ngOnChanges – the starting application

What are SimpleChanges?

You probably noticed the SimpleChanges interface in the parameters list. The interface is a great and powerful tool that is used to view changes every time the declared property values are changed. If we take a look at the console when we initially run the application we will see three properties inside the SimpleChanges object.

  • currentValue – the current value of the property. Because we provided the string “Tom” from the get-go, the current value will be Tom.
  • previousValue – the value that the property had previously. Currently, it is undefined because we haven’t changed the property.
  • firstChange – a boolean value representing whether the change occurred only once or multiple times

So, how can we use the power of tracking the changed properties? For example we can trigger a side action every time the property changes:

  ngOnChanges(changes: SimpleChanges) {
    if (changes.name.firstChange) {
      this.names.push('Initial Change: ' + changes.name.currentValue);
    } else {
      this.names.push('Following Change: ' + changes.name.currentValue);
    }
  }

The code above will intercept the property change (sending “Tom” to the child component), and it will push that value to the names array.

ngOnChanges – Initial Change

Next, let us set up the form so we can send additional names to the child component.

  submitNewName(event: any) {
    if (!this.userForm.valid) {
      return;
    }

    this.name = this.userForm.value.name;
  }

We are changing the name property every time we submit our form. What happens then? Well, we send the property down to the UserListComponent, which triggers the onChanges lifecycle hook (we changed the property value, keep in mind the property is decorated with @Input()), and that we get the changes object. We are checking for the firstChange, and reacting according to that. (Our first change will always start with Initial Change and the others with Following Change).

ngOnChanges – intercepting subsequent changes

The StackBlitz for this project can be found below: