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:
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 isundefined
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.
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).
The StackBlitz for this project can be found below: