I have created a simple example to demonstrate a weird issue I\'m facing.
Stackblitz - https://stackblitz.com/edit/angular-change-detection-form-group
I hav
I found a workaround to this problem although I am not sure if this is the ideal solution.
We can listen to the form group value changes and then trigger change detection in the input component
this.form.valueChanges.subscribe( () => {
this.cdr.detectChanges()
});
This way it updates the label values as well along with the inputs.
Here is the solution:
https://stackblitz.com/edit/angular-change-detection-form-group-value-change-issue-resolved
I'm not sure if it's a bug from Angular but happy that I figured out some workaround :)
During the automatic change detection (cd) run Angular would notice that the values of your FormGroup
have been updated and updates the UI respectively.
By setting the change detection strategy to OnPush
you deactivates the automatic change detection run by Angular. In this case only the internal values are updated, but Angular would not check the values of the UI.
Maybe this is a too superficial explanation of Angular's ChangeDetection, so I recommend this blog for a deeper look into this topic.
ngOnChanges
does not trigger, because the object reference (memory address) of your FormGroup
has not been changed. ngOnChanges
is only triggered if you pass primitives to the @Input
of your components. And this would also trigger a new change detection run by Angular.
To update the UI you can trigger the change detection manually by injecting ChangeDetectorRef in your parent component and calling detectChanges()
.
This could look like this:
constructor(private cd: ChangeDetectorRef) {}
...
changeFormValue() {
this.form.setValue({
name: 'XYZ',
age: 35
});
// This will trigger the change detection and your input field are updated
this.cd.detectChanges();
}
I hope this is understandable ;-)
Angular only detects changes if the memory address of the variable changes. Setting the value does not change memory address, thus does not hit ngOnChanges.
Same goes with arrays. A simple push does not hit ngOnChanges, have to change memory address by = to new array.
Try this:
import { Component, Input, OnInit, OnChanges, ChangeDetectionStrategy } from '@angular/core';
import { FormGroup } from '@angular/forms';
@Component({
selector: 'app-input',
template: `
<div formGroupName="form">
Name : <input type="text" formControlName="name" /> {{form.value.name}} <br /><br />
Age : <input type="text" formControlName="age" /> {{form.value.age}}
</div>`,
styles: [``],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class InputComponent implements OnInit, OnChanges {
@Input() form: FormGroup;
ngOnInit() {
}
ngOnChanges(changes) {
console.log(changes)
}
}
You could do something better !, let me know if this works :
when you use reactive forms you can use a really cool method called updateValueAndValidity();
private changeControlValue(control, value: number) {
control.setValue(value);
control.updateValueAndValidity();
}
You can also use this method when updating the validators added to a form, example:
this.control.setValidators([Validators.required, Validators.minLength(5)]);
control.updateValueAndValidity();
This should do the trick ! I think this is one of the best adventages of using reactive forms or form controls against ng-model.
I don't recommend to use valueChanges at your form as you are open to forget to de-subscribe and create a memory leak, even you remember it can be tedious to create this flow.
And remember, when using onPush change detection only three things are going to be detected as changes by Angular:
1- Inputs and Outputs. 2- Html events that the user can do, like (click). 3- Async events like subscriptions.
I hope i helped you !.