Components: Expression has changed after it was checked for MatButton on disabled change through form validity change

Created on 18 Feb 2019  路  12Comments  路  Source: angular/components

No error should arise!

When a dynamic input in a form changes its validity, Angular throws an exception.

Steps to reproduce:

https://stackblitz.com/edit/expression-was-checked-material
Fill out Question, tick the extra check box, check the console for error logs:
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'disabled: false'. Current value: 'disabled: true'.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

    "@angular/cdk": "7.3.2",
    "@angular/common": "^7.0.1",
    "@angular/core": "^7.0.1",
    "@angular/material": "^7.3.2"

Is there anything else we should know?

When there is no MatButton, there is no error.

P3 forms

Most helpful comment

Came across the same issue when the disabled logic of a field changes after an Observable/api return. Putting the timeout on the action that causes a change (in the observable subscribe) in the disabled logic worked for me as well.

All 12 comments

@eydrian MatButton has nothing to do with this error, its because of the way angular's change detection works in dev mode. Go through this Blog, this will help you understand, more about the error and will suggest some common approaches to tackle the error.
Here's one of the the ways to avoid this error Updated code on stackblitz

@Iamakr your updated blitz "solves" the issue, by simply removing the functionality that causes it. The 3rd field is not conditionally required anymore.

@Iamakr there was a mistake in my blitz but unrelated - I fixed it. I updated your blitz to make it actually work and it ends up with the same error ...
https://stackblitz.com/edit/expression-was-checked-material-by75ta
The fun fact is, when you remove the attribute mat-button from the button, it works as expected. Thats why I think it is MatButton related.
I solved it differently with onModelChange listeners and then actively setting the button to disabled or enabled which kinda covers/implements what I got from the different Blog post. But still... without a MatButton it works as expected

@Iamakr your updated blitz "solves" the issue, by simply removing the functionality that causes it. The 3rd field is not conditionally required anymore.

Sorry, I didn't verify the answer before posting.

@Iamakr there was a mistake in my blitz but unrelated - I fixed it. I updated your blitz to make it actually work and it ends up with the same error ...
https://stackblitz.com/edit/expression-was-checked-material-by75ta
The fun fact is, when you remove the attribute mat-button from the button, it works as expected. Thats why I think it is MatButton related.
I solved it differently with onModelChange listeners and then actively setting the button to disabled or enabled which kinda covers/implements what I got from the different Blog post. But still... without a MatButton it works as expected

Yes I can see removing mat-button fixes the error. However I would like to see how you solved this error using onModelChange listeners.

@lamakr it's not a solution I'm particularly proud of ... it's rather verbose
It goes in the following direction:

each in put in the html has the following attribute

(ngModelChange)="updateValidity($event)"

and then in the component:

  public updateValidity(_event: Event) {
    setTimeout(() => {
      this.formIsValid = this.registrationForm.valid;
    }, 0);
  }
  public isSubmitEnabled() {
    return this.formIsValid;
  }

and then the submit button:

      <button mat-raised-button
              (click)="action(models)"
              [disabled]="!isSubmitEnabled()">Continue</button>
    </div>

through the timeout it works... but it's a bit hacky

Bump, I see this error too

Came across the same issue when the disabled logic of a field changes after an Observable/api return. Putting the timeout on the action that causes a change (in the observable subscribe) in the disabled logic worked for me as well.

I think #10914 is the same issue. And as I mention it in the other issue, well the problem is still on the version 8 of angular Material (which is normal)

I looked into this and updated the StackBlitz: https://stackblitz.com/edit/comp-15222?file=src/app/app.component.html.

This StackBlitz replaced mat-button with any arbitrary Angular directive. The same error happens. We don't do anything special from within our button. Simply a disabled @Input seems to cause the issue. This should actually be reported as an Angular issue.

I had the same issue, i fixed it by doing:
add this in the input that will update requirements (html)
(ngModelChange)="updateRequiredValues($event)"

in (TS) add this:

updateRequiredValues(event) {
        if (condition) {
            this.configForm.get('title').setValidators([]);
            this.configForm.get('title').updateValueAndValidity();
        } else {
            this.configForm.get('title').setValidators([Validators.required]);
            this.configForm.get('title').updateValueAndValidity();
        }
    }

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

savaryt picture savaryt  路  3Comments

vanor89 picture vanor89  路  3Comments

vitaly-t picture vitaly-t  路  3Comments

kara picture kara  路  3Comments

michaelb-01 picture michaelb-01  路  3Comments