How $onChanges works in AngularJS components

Loading

In AngularJS 1.5+, $onChanges() is a lifecycle hook used in components to detect changes in one-way bound (<) input properties. It executes whenever the component receives new data from its parent.


1. What is $onChanges?

$onChanges(changesObj) is triggered when the bound inputs (<) change. The changesObj argument contains:

  • currentValue: The new value of the property.
  • previousValue: The old value of the property.
  • isFirstChange(): Returns true if it is the first time the property changes.

2. When is $onChanges Triggered?

  • When the parent updates a bound (<) property.
  • When the component initializes with data from the parent.
  • When the reference of an object/array changes.

3. Example of $onChanges in a Component

Parent Component (Passing Data)

app.component('parentComponent', {
template: `
<h2>Parent Component</h2>
<input type="text" ng-model="$ctrl.userName">
<button ng-click="$ctrl.changeUser()">Change User</button>
<child-component name="$ctrl.userName"></child-component>
`,
controller: function() {
this.userName = "John Doe";

this.changeUser = function() {
this.userName = "Jane Smith"; // Changing value
};
}
});

Child Component (Detecting Changes)

app.component('childComponent', {
bindings: {
name: '<'
},
template: `<p>Child Component: {{ $ctrl.name }}</p>`,
controller: function() {
this.$onChanges = function(changes) {
if (changes.name) {
console.log("Old Name:", changes.name.previousValue);
console.log("New Name:", changes.name.currentValue);
}
};
}
});

How it Works:

  1. Initial Load: this.name = "John Doe", $onChanges runs with previousValue: undefined, currentValue: "John Doe".
  2. Click “Change User” Button: this.userName updates to "Jane Smith", triggering $onChanges.
  3. Console Logs: pgsqlCopyEditOld Name: John Doe New Name: Jane Smith

4. $onChanges with Objects & Arrays

If you pass an object or array, $onChanges only triggers when the reference changes, not the values inside.

Example (Object Reference Change)

app.component('parentComponent', {
template: `
<h2>Parent Component</h2>
<button ng-click="$ctrl.updateUser()">Update User</button>
<child-component user="$ctrl.user"></child-component>
`,
controller: function() {
this.user = { name: "John Doe" };

this.updateUser = function() {
this.user = { name: "Jane Smith" }; // New object reference
};
}
});

Child Component

app.component('childComponent', {
bindings: {
user: '<'
},
template: `<p>User Name: {{$ctrl.user.name}}</p>`,
controller: function() {
this.$onChanges = function(changes) {
if (changes.user) {
console.log("Old User:", changes.user.previousValue);
console.log("New User:", changes.user.currentValue);
}
};
}
});

How it Works:

  • When this.user = { name: "Jane Smith" }, the reference changes, triggering $onChanges.
  • Console logs: Old User: { name: "John Doe" } New User: { name: "Jane Smith" }
  • BUT, if we modify this.user.name = "Jane Smith";, $onChanges won’t trigger because the reference stays the same.

5. Checking First-Time Changes

Use .isFirstChange() to detect the first value change.

Example

this.$onChanges = function(changes) {
if (changes.name) {
if (changes.name.isFirstChange()) {
console.log("First time setting name:", changes.name.currentValue);
} else {
console.log("Name changed from", changes.name.previousValue, "to", changes.name.currentValue);
}
}
};

6. Key Takeaways

  • $onChanges(changesObj) is triggered whenever a bound (<) input changes.
  • Works best with primitives (strings, numbers, booleans).
  • Objects and arrays trigger $onChanges only when their reference changes.
  • Use .isFirstChange() to detect the first change.

7. When to Use $onChanges?

When you need to react to input changes inside a component.
When working with parent-child data flow.
When performing data transformations or API calls based on new input.

Avoid using $onChanges for objects that change frequently without changing reference.


8. Alternative: Using $watch (If Reference Doesn’t Change)

If you want to detect changes within an object, use $watch inside $onInit():

this.$onInit = function() {
this.$watch('$ctrl.user.name', function(newVal, oldVal) {
console.log("User Name Changed:", oldVal, "->", newVal);
});
};

Leave a Reply

Your email address will not be published. Required fields are marked *