13

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?

John Arrowwood
  • 2,370
  • 2
  • 21
  • 32
  • Remove the value="{{item.name}}" from the input as it's making an infinite loop. ngModel is enough – Mehdi Oct 24 '17 at 22:46

5 Answers5

25

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.

andreisrob
  • 1,615
  • 15
  • 11
  • This wasn't applicable for me using angular 7, although it did help me identify my issue when using `` within a ``. – Tim Harker Jun 04 '19 at 04:36
  • I relate to the first line as well "This error drove me absolutely crazy". Mine was a text box, and luckily it was a readonly. So i changed it to [value]. – Ravi Sankar Rao Dec 03 '19 at 00:46
1

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>
kemsky
  • 14,727
  • 3
  • 32
  • 51
1

The problem is mostly occurring as a result of setting two different value to same parameter of a component during view initializing.

There is a really long explanation of problem and alternative solutions on angular-university blog .

nzrytmn
  • 6,193
  • 1
  • 41
  • 38
0

In case anyone finds themselves here because they have a similar problem:

First thing to check is to make sure that the use of [(ngModel)]="value" works as shown in the docs. If it does not, make sure you have imported FormsModule. The Angular Material docs neglect to mention it, but ngModel doesn't work without it.

See also Why (ngModel) is not working?

John Arrowwood
  • 2,370
  • 2
  • 21
  • 32
0

I had this error:

ExpressionChangedAfterItHasBeenCheckedError ... Previous value: 'aria-describedby: null' Current value: 'aria-describedby: mat-error-22'

the problem was that I was manually validating my form immediately after creation of the form and before loading of mat-form-field components. In my case, I actually didn't have to validate my form so I removed it.

Vahid
  • 6,639
  • 5
  • 37
  • 61