Cannot Reset Input value based on select box when form is split into child components
I am showing and hiding a FormControl based on another FormControl in the same FormGroup which works using the method below, but when I try to reset the input is hiding, so send its value not sent. I am getting below.
Component method
public hasOtherFundingSource(index: number) {
const formGroup = this.overviewFunds.at(index);
const source = formGroup.get('source').value;
if (source !== 'other') {
formGroup.get('other_source_desc').reset(); // Throws error
return false;
} else {
return true;
}
}
Mistake
ExampleComponent.html:12 ERROR Error:
ExpressionChangedAfterItHasBeenCheckedError:
Expression has changed after it was checked.
Previous value: 'true'. Current value: 'false'.
With some form of @yurzui help, I created a plunker that shows the error. Just change other
to another
in the select box and watch the console log out. It looks like the bug has to do with splitting the FormGroup into subcomponents to reduce size and logic in each class.
If you look at the first plunker that was created, the error does not occur if the form is encapsulated in an application component.
source to share
You should avoid any side effect in functions that will be executed every time you view views
<div *ngIf="hasOtherFundingSource(i)">
Function hasOtherFundingSource
will run twice in dev mode on each type of application.
There is a directive NgControlStatus
that checks the validation status.
On first checkout, your form is valid
After that you call formGroup.get('other_source_desc').reset();
and the status becamesinvalid
Then angular works view.checkNoChanges()
and you getExpressionChangedAfterItHasBeenCheckedError
So according to https://angular.io/docs/ts/latest/guide/template-syntax.html#!#no-visible-side-effects
No visible side effects
The template expression must not modify any application state other than the value of the target property.
This rule is important for angular's "unidirectional data flow" policy. You should never worry that reading a component's value might change another displayed value. The view must be stable throughout a single render.
To solve your problem, I did the following:
1) Added event ngModelChange
to select
control
<select formControlName="source" (ngModelChange)="sourceChanged($event, i)">
2) Move side effect from hasOtherFundingSource
to SourceChanged function
sourceChanged(value: string, index: number) {
if (value !== 'other') {
const formGroup = this.overviewFunds.at(index);
formGroup.get('other_source_desc').reset();
}
}
source to share