Parent component template:
<my-component [param]="whatever"></my-component>
Parent component code:
whatever = { value1: 0, value2: 1, value3: 'foo' };
Child component code:
@Input() public param: any;
ngOnChanges() {
console.log('Received input: ', param);
}
This isn't working. Any change to whatever
will be unnoticed by the child component and ngOnChanges
won't fire.
It also doesn't work if I try to use a setter:
@Input() set param(value) {
console.log(value);
}
And it doesn't help if I try to run a manual zone update in the parent component.
Apparently @Input()
can only detect when an object's structure has changed, but not its values.
So how can I pass an object as an @Input()
property and have the child component detect value changes?
Current behaviorWhen dynamically creating components, manually changing a property of a child component of that created component and then triggering changeDetection, ngOnChanges on this child component does not get called.
The ng-change event is triggered at every change in the value. It will not wait until all changes are made, or when the input field loses focus. The ng-change event is only triggered if there is a actual change in the input value, and not if the change was made from a JavaScript.
ngOnChanges gets called before ngOnInit and whenever a component's bound input is changed FROM THE PARENT COMPONENT. Remember that ngOnChanges is specific to bound inputs on the component. This means if you don't have any @Input properties on a child, ngOnChanges will never get called.
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. The child Component decorates the property using the @Input decorator.
The OnChanges lifecycle hook is triggered when the @Input
property value changes. In the case of an object, that value is the object reference. If the object reference does not change, OnChanges
is not triggered.
A possible technique to force change detection is to set a new object reference after modifying the property values:
this.whatever.value1 = 2;
this.whatever.value2 = 3;
this.whatever = Object.assign({}, this.whatever);
The ngOnChanges
event handler can then be used to monitor the changes:
ngOnChanges(changes: SimpleChanges) {
for (let propName in changes) {
let chng = changes[propName];
let cur = JSON.stringify(chng.currentValue);
console.log(propName, cur);
}
}
As an alternative, if @Input
decorates a getter/setter property, the changes can be monitored in the setter:
private _param: Object;
@Input() get param(): Object {
return this._param;
}
set param(value: Object) {
console.log("setter", value);
this._param = value;
}
See this stackblitz for a demo.
Angular change detection is triggered when the @Input property value changes.
So to trigger change detection in case of an object you could pass a copy of the object using spread operator as the input.
for eg. someVar = {key: value}
this is @Input()
variable, so pass like
<app-sample [someVar]="{...someVar}" ></app-sample>
{...VARIABLE}
<- here is the magic
if spread operator won't work use any object deep copying methods like
JSON.parse(JSON.stringify(obj))
Like this:
@Input() public param: any;
ngOnChanges(changes: SimpleChanges): void {
// Do your check here
console.log(changes.param.previousValue);
}
Using changes gets you access to previousValue and currentValue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With