Reputation: 18328
I'm 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 being hidden so on submit it's value is not sent I get the error 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;
}
}
Error
ExampleComponent.html:12 ERROR Error:
ExpressionChangedAfterItHasBeenCheckedError:
Expression has changed after it was checked.
Previous value: 'true'. Current value: 'false'.
With some help form @yurzui I created a plunker that shows the error. Just change other
to another
in the select field and watch the console log out the error. The error seems to be related to splitting the FormGroup into sub-components to reduce the size and logic in each class.
If you look at the first plunker that was created the error doesn't occur when the form is encapsulated in the app-component.
Upvotes: 3
Views: 2254
Reputation: 214335
You should avoid any side effect within functions that will be executed on every view checking
<div *ngIf="hasOtherFundingSource(i)">
hasOtherFundingSource
function will be executed twice in dev mode on every application tick.
There is NgControlStatus
directive that checks validation status.
On the first check your form is valid
After that you're calling formGroup.get('other_source_desc').reset();
and status becames invalid
Then angular is running view.checkNoChanges()
and you get ExpressionChangedAfterItHasBeenCheckedError
So according to https://angular.io/docs/ts/latest/guide/template-syntax.html#!#no-visible-side-effects
A template expression should not change any application state other than the value of the target property.
This rule is essential to Angular's "unidirectional data flow" policy. You should never worry that reading a component value might change some other displayed value. The view should be stable throughout a single rendering pass.
To solve your problem i did the following:
1) Added ngModelChange
event 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();
}
}
Upvotes: 3