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.
No ExpressionChangedAfterItHasBeenCheckedError
when using *ngIf on an input
ExpressionChangedAfterItHasBeenCheckedError
^7.1.4
^7.2.0
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.
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.
Same error for same reason, any news?
Thanks!
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.
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.
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
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 of control.stateChanges
, hintChildren.changes
and errorChildren.changes
which all are emitted during the first initialisation of the formfield. But maybe that can be mitigated somehow.
That a bug,
im running into the same problem
<mat-form-field style="width: 100%;" [ngClass]="{myInputNoWrapper: options.noWrapper}">
<mat-label *ngIf="options.label">{{options.label}}</mat-label>
<span *ngIf="options.prefix" matPrefix>{{ options.prefix }}   </span>
<input *ngIf="options.type != 'textarea'; else ta" matInput [formControl]="myInputControl"
(input)="onChange($event)"
(blur)="onBlur($event)"
[type]="options.type" [placeholder]="options.placeholder"
[required]="options.required">
<ng-template #ta>
<textarea [ngStyle]="{'height': options.textareaH}" matInput [formControl]="myInputControl"
(input)="onChange($event)"
(blur)="onBlur($event)"
[placeholder]="options.placeholder"
[required]="options.required"
></textarea>
</ng-template>
<mat-hint *ngIf="options.hint">{{options.hint}}</mat-hint>
<mat-error *ngIf="!options.noValidate" [MYDIRECTIVE]="myInputControl"></mat-error>
</mat-form-field>
ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'aria-describedby: null'. Current value: 'aria-describedby: mat-hint-0'
if the only workaround is code duplication ...
The same problem appears with both mat-hint
and mat-error
elements.
To avoid ExpressionChangedAfterItHasBeenCheckedError
beeing thrown you should avoid using *ngIf
directives with matInput
elements
"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 your mat-error
/mat-hint
.
"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
.
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 the mat-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 from false
to true
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.
@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:
@Component()
class MyComponentWithInputField implements AfterViewInit {
myControl: AbstractControl;
constructor(private cdr: ChangeDetectorRef) { }
ngAfterViewInit() {
if (this.myControl.errors) {
this.cdr.detectChanges();
}
}
}
Try use Unique ID for the hint. A simple hack to make the error go away
stackblitz
<mat-hint [id]="null">No error</mat-hint>
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:
<mat-form-field appearance="outline">
<mat-label>State/Province/Region</mat-label>
<input matInput type="text" required formControlName="state_province" *ngIf="stateProvinceRegions.length == 0; else stateProvinceRegionSelector"/>
<ng-template #stateProvinceRegionSelector>
<mat-select required formControlName="state_province">
<mat-option *ngFor="let entry of stateProvinceRegions" [value]="entry.key" >
{{entry.value}}
</mat-option>
</mat-select>
</ng-template>
<!-- [id]="null" is a hack (https://github.com/angular/components/issues/16209)-->
<mat-error [id]="null" *ngIf="purchaseForm.get('address.state_province')?.errors?.required">Region is required.</mat-error>
</mat-form-field>
I suspect this would be solved by addressing this framework issue: https://github.com/angular/angular/issues/37319
for now i still have the same problem.
Fastest fix for me was instead of using *ngIf for the matInput's, use fxHide.
ofc you need to be using the flexLayout module, but simulair solutions should work.
for now i still have the same problem.
Fastest fix for me was instead of using *ngIf for the matInput's, use fxHide.
ofc you need to be using the flexLayout module, but simulair solutions should work.
This issue can be solved simply by injecting into the constructor private cdRef: ChangeDetectorRef
And in the ngAfterViewInit cycle hook just call the method detectChanges()
ngAfterViewInit(): void { this.cdRef.detectChanges(); }
Error:
<mat-hint *ngIf="dropdownOptions.hint">{{dropdownOptions.hint}}</mat-hint>
No Error using @ZloDeeV 's answer.
<mat-hint *ngIf="dropdownOptions.hint" [id]="null">{{dropdownOptions.hint}}</mat-hint>
Most helpful comment
Try use Unique ID for the hint. A simple hack to make the error go away
stackblitz