I have created a component that represents the form for modifying the details of an object. The object exists in app.component.ts
:
export class AppComponent {
selectedItem: Item;
}
It is passed in via two-way binding into the component from app.component.html
like so:
<item-details [(item)]="selectedItem"></item-details>
Within the component, the individual fields of the Item
are bound to input controls, to allow the user to update the data, e.g.:
<mat-form-field class=name>
<input matInput [(ngModel)]="item.name" value="{{item.name}}" required placeholder="Name">
<mat-error>Item name can not be left blank</mat-error>
</mat-form-field>
Everything works great until I get to the textarea:
<mat-form-field class="full-width">
<textarea id=description matInput [(ngModel)]="item.description" placeholder="Description">{{item.description}}</textarea>
</mat-form-field>
It WORKS, but it throws an exception:
ExpressionChangedAfterItHasBeenCheckedError
The error is not directly tied to the <textarea>
, as it says that the value went from false
to true
and as such appears to be related to the valid
property on the form, as hinted at here.
Interestingly, I can avoid the error by modifying the contents of the <textarea></textarea>
such as by putting a space after the contents:
<textarea ...>{{item.description}} </textarea>
But that only works if item.description
is not null
. When it is null
then I get the error again.
I'm triggering the change to selectedItem
from another child component, which also has bi-directional binding on selectedItem
. When the user selects an item, the new Item
flows up to the app, and back down to the detail component.
I have read Everything you need to know about the 'ExpressionChangedAfterItHasBeenCheckedError' error article. To quote the article "I don't recommend using them but rather redesign your application".
Great! How? How do I structure things so that control A is used to select an Item
, and control B is used to edit it? What is the right way for controls A and B talk to one another without triggering this error?
This error drove me absolutely crazy, and it only revealed itself after upgrading to Angular 5. In my case, I am not using [(ngModel)]
. I am using the textarea for display purposes only (to get Material look and feel). Nevertheless, this may be helpful for others searching endlessly like I have.
I discovered that if you bind to the [value]
property of the textarea instead of using interpolation {{}}
, the error goes away.
Instead of: <textarea>{{value}}</textarea>
Do: <textarea [value]="value"></textarea>
You didn't have any problems with <input>
because it is a single-tag element and therefore must use property binding and not interpolation. (To be clear, your use of interpolation together with value
is only necessary because you are binding to an attribute which is evaluated only once. Bind to [value]
, which is a property, and you no longer need the {{}}
. See the angular docs where this is explained very clearly.)
I suspect that with interpolation, angular sets the form as false
on the first digest cycle, and on the second digest as true
when it recognizes the value. With [value]
property binding, it recognizes the value on the first digest.
In any case, it works.
If you are using ngModel
then {{item.description}}
is useless, should be enough:
<mat-form-field class="full-width">
<textarea id=description matInput [(ngModel)]="item.description" placeholder="Description"></textarea>
</mat-form-field>
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