components: Mat-hint causing an ExpressionChangedAfterItHasBeenCheckedError if respective input has a *ngIf directive
Reproduction
Minimal repro on StackBlitz. Open the console to see the following error message:
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'aria-describedby: null'. Current value: 'aria-describedby: mat-hint-0'.
This always happens if the respective input has a *ngIf
-directive. I couldn’t observe any subsequent errors caused by this. Using the hintLabel
property instead of a <mat-hint>
results in the same error.
Expected Behavior
No ExpressionChangedAfterItHasBeenCheckedError
when using *ngIf on an input
Actual Behavior
ExpressionChangedAfterItHasBeenCheckedError
Environment
- Angular:
^7.1.4
- CDK/Material:
^7.2.0
- Browser(s): Chrome (Most recent @ Version 75.0.3770.80)
- Operating System (e.g. Windows, macOS, Ubuntu): macOS
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 12
- Comments: 22 (3 by maintainers)
Commits related to this issue
- Avoid ExpressionChangedAfterItHasBeenCheckedError on clear() Unfortunately because of https://github.com/angular/components/issues/16209 we cannot very well use *ngIf inside a <mat-form-field> so we ... — committed to Ecodev/natural by PowerKiKi 5 years ago
- feat(material/chips): update chip-list describedby to match input Fixes #16209: ExpressionChangedAfterItHasBeenCheckedError that occurs when chip-lists are content-projected into a mat-form-field — committed to RobertAKARobin/components by RobertAKARobin 2 years ago
- feat(material/chips): update chip-list describedby to match input (#24292) Fixes #16209: ExpressionChangedAfterItHasBeenCheckedError that occurs when chip-lists are content-projected into a mat-form-... — committed to amysorto/components by RobertAKARobin 2 years ago
- feat(material/chips): update chip-list describedby to match input (#24292) Fixes #16209: ExpressionChangedAfterItHasBeenCheckedError that occurs when chip-lists are content-projected into a mat-form-... — committed to forsti0506/components by RobertAKARobin 2 years ago
Try use Unique ID for the hint. A simple hack to make the error go away stackblitz
This should not be marked as closed. While true that running in production mode won’t raise this error, that’s just because production is no longer running with this safety check on (for efficiency). The bug still exists and should still be addressed.
This is because Angular is running in development mode, and in this mode change detection adds an additional turn after every regular change detection run to check if the model has changed.
If you run this example locally on you machine in production mode, everything will be ok.
I’m also having this same issue. I’m using it the same way as @ritxweb was however I believe using it this way should not raise this error.
Mainly because I’ve made a dynamic form builder and every generated component shared the name <mat-error> component, and if I were to put one <mat-form-field> on each field condition, I would also have to put one <mat-error>, copying and pasting the same code over and over.
We are building a component library which is very inspired by angular material 😃 we use the concept of the formfield with some modifications but run into the same issue.
What I found out is that with the ngIf on the input the instantiation changes.
With ngIf the cycle is like:
Without ngif the cycle is:
A possible solution I found is to defer all calls of
_syncDescribedByIds
to the next CD cycle. The downside I see is additional CD cycles especially during the initialisation of the formfield because it is called in the subscription ofcontrol.stateChanges
,hintChildren.changes
anderrorChildren.changes
which all are emitted during the first initialisation of the formfield. But maybe that can be mitigated somehow.Would be wonderful for this to be addressed.
A use-case where an *ngIf would be needed is in a case dealing with Countries and States --where a text-entry of State is allowed but then switches to a drop-down for certain countries (e.g. US) as follows:
I think its not about avoiding
ngIf
its about learning when it is pertinent or not.when an
ngIf
statement is false, the component ceases to Exist in DOM. and if it doesn’t exist when it is needed then the library will crash (although this could be treated better internally), using a<ng-template>
for themat-error
component is better, because the component will always be there available, and can be referenced by the library.Every time a
ngIf
statement goes fromfalse
totrue
the component will be created form scratch, which sometimes can lead to performance issues. Thats why its better to learn when it is ok or not to use it.Hello
I am having this error using mat-form-field with several inputs with a *ngIf each:
<mat-form-field> <input *ngIf="displayLanguage === 1" matInput type="text" formControlName="city" name="city" required> <input *ngIf="displayLanguage === 2" matInput type="text" formControlName="city2" name="city2"> <input *ngIf="displayLanguage === 3" matInput type="text" formControlName="city3" name="city3"> </mat-form-field>
But when I create a mat-form-field for each input and apply the *ngIf to them, the error disappears:
<mat-form-field *ngIf="displayLanguage === 1"> <input matInput type="text" formControlName="city" name="city" required> </mat-form-field> <mat-form-field *ngIf="displayLanguage === 2"> <input matInput type="text" formControlName="city2" name="city2"> </mat-form-field> <mat-form-field *ngIf="displayLanguage === 3"> <input matInput type="text" formControlName="city3" name="city3"> </mat-form-field>
Hope this helps.
Error:
No Error using @ZloDeeV 's answer.
The same problem appears with both
mat-hint
andmat-error
elements. To avoidExpressionChangedAfterItHasBeenCheckedError
beeing thrown you should avoid using*ngIf
directives withmatInput
elements@joaodforce Thank you so much, it has actually led me to a much better solution!
However, I’d still say there is a but in angular-material here, and I’m not sure if avoiding ngIf/ngFor is always possible. I could not get it to work within mat-stepper, which actually relies on step components being in DOM while I need to dynamically control what steps there are.
Here’s another workaround:
“Avoid using
*ngIf
” is not a solution at all. It’s a pretty essential feature of Angular, you can’t just avoid it everywhere above yourmat-error
/mat-hint
.That a bug,
im running into the same problem
if the only workaround is code duplication …