Angular ngOnChanges life Cycle Hook

In this article, we are going to look at how ngOnChanges Life cycle hook works in Angular. The Angular fires ngOnChanges or OnChanges, when the Angular detects changes to the @Input properties of the Component.  It gets the changes in the form of simplechanges object. We will be learning all these in this tutorial.

What is nOnChanges Life cycle hook

The ngOnChnages is a life cycle hook, which angular fires when it detects changes to data-bound input property. This method receives a SimpeChanges object, which contains the current and previous property values.

There are several ways the parent component can communicate with the child component. One of the ways is to use the @Input decorator. We looked at this in our tutorial passing data to Child Components

Let us just recap what we have done in that tutorial

The child Component decorates the property using the @Input decorator.

And then parent passes the data to the child component using property binding as shown below

Whenever the parent changes the value of the message property,  the Angular raises the OnChanges hook event in the child component, so that it can act upon it.

How does it work

The ngOnChanges() method takes an object that maps each changed property name to a SimpleChange object, which holds the current and previous property values. You can iterate over the changed properties and act upon it.

SimpleChange

SimpleChange is a simple class, which has three properties

Property NameDescription
previousValue:anyPrevious value of the input property.
currentValue:anyNew or current value of the input property.
FirstChange():booleanBoolean value, which tells us whether it was the first time the change has taken place

SimpleChanges

Every @Input property of our component gets a SimpleChange object (if Property is changed)

SimpleChanges is the object that contains the instance of all those SimpleChange objects. You can access those SimpleChange objects using the name of the @Input property as the key

For Example, if the two Input properties message1 & message2 are changed, then the SimpleChanges object looks like

And if the input property is an object (customer object with name & code property) then the SimpleChanges would be

ngOnChanges example

Create a class customer.ts under src/app folder.

Parent Component

Lets us look at the code

We have 3 user input fields for the message, code and name. The UpdateCustomer button updates the Customer object.

The message and Customer is bound to the child component using the property binding

The AppComponent class has a message & customer property. We update customer object with new code & name when user clicks the updateCustomer button.

Child Component

Let us look at each line of code in detail

First, We import the Input, OnInit, OnChanges, SimpleChanges, SimpleChange from Angular Core

The Template displays the message & name property from the customer object. Both these properties are updated from the parent component.

We also display the changelog using ngFor Directive.

The child Component implements the OnChanges & OnInit life cycle hooks.

We also define message & customer property, which we decorate with the @Input decorator. The parent component updates these properties via Property Binding.

The OnInit hook

The ngOnChnages hook gets all the changes as an instance of SimpleChanges. This object contains the instance of SimpleChange for each property

We, then loop through each property of the SimpleChanges object and get a reference to the SimpleChange object.

Next, we will take the current & previous value of each property and add it to change log

That’s it.

Now our OnChanges hook is ready to use.

Now, run the code and type the Hello and you will see the following log

Open the developer console and you should see the changes object

Note that the first OnChanges fired before the OnInit hook. This ensures that initial values bound to inputs are available when ngOnInit() is called

OnChanges does not fire always

Now, change the customer code and name and click UpdateCustomer button.

The Child Components displays customer Name, but OnChanges event does not fire.

This behavior is by design.

Template is Updated

Updating the DOM is part of Angular’s change detection mechanism The change detector checks each and every bound property for changes and updates the DOM if it finds any changes.

In the child component template, we have two bound properties. {{ message }} & {{ customer.name }}. Hence the change detector checks only these two properties and updates the DOM. The customer object also has code property. The change detector will never check it.

Why onChanges does not fire?

The Change detector also raises the OnChanges hook. But it uses a different techniques for comparison.

The change detector uses the === strict equality operator for detecting changes to the input properties. For primitive data types like string, the above comparison works perfectly

But in the case of an object like a customer, this fails. For Arrays/objects, the strict checking means that only the references are checked. Since the reference to the customer stays the same the Angular does not raise the OnChanges hook.

That leaves us two possible solutions

  1. Create a new customer and copy the old data to new customer
  2. We can Perform our own change detection using the ngDoCheck lifecycle hook

Update the updateCustomer method and create a new instance of customer every time

Now, run the code, you will see onChanges event fired when customer is updated

The second method is to use the ngDoCheck lifecycle hook, which we will cover in the next tutorial

Source Code

Source Code

Summary

In this tutorial, we learned how to use ngOnChanges method. We also, learned how to find out which properties are changed using SimpleChanges object

References

  1. OnChanges API
  2. SimpleCharges API
  3. SimpleChange

1 thought on “Angular ngOnChanges life Cycle Hook”

  1. Thank you very much, very very good tuto
    I think the constructor of Customer shoudd be implemented like that :
    new Customer(this.code = null , this.name=”);
    Or make the properties optionnals in the model :
    export class Customer {
    constructor (public code?: number, public name?: string) {
    }
    }
    Thanks

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top