Reputation: 139
I am encountering an issue with my Angular application where I'm dynamically changing the required
attribute of a select field in a form after it has been submitted.
evaluations.component.html
<form
novalidate
#form="ngForm"
(ngSubmit)="form.form.valid && onSubmit()">
<div *ngFor="let evaluation of evaluations; index as index">
<select
name="evaluator{{index}}"
id="evaluator{{index}}"
#evaluatorField="ngModel"
[(ngModel)]="evaluatorId"
[required]="isEvaluatorRequired"
[ngClass]="{'is-invalid': form.submitted && evaluatorField.invalid}">
<option *ngFor="let evaluator of evaluators" [ngValue]="evaluator._id">
{{evaluator.name}}
</option>
</select>
...
</div>
<div>
<input type="radio" name="operation" #operation="ngModel" id="operation-suggested" [value]="'APPROVE'"
[(ngModel)]="operation" (ngModelChange)="onOperationChange($event)" required />
<input type="radio" name="operation" #operation="ngModel" id="operation-decline" [value]="'DECLINE'"
[(ngModel)]="operation" (ngModelChange)="onOperationChange($event)" required />
...
</div>
...
<button type="submit" id="save">
Save
</button>
</form>
isEvaluatorRequired
changes on the call to onOperationChange(...)
done when the operation value changes.
evaluations.component.ts
...
onOperationChange(operation: OperationStatus) {
this.isEvaluatorRequired = [OperationStatus.APPROVE,/*...*/].includes(operation);
}
...
The problem arises when I change the operation value after submitting the form, which in turn modifies the isEvaluatorRequired
value. This triggers the following error:
ERROR Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Previous value: 'true'. Current value: 'false'. Expression location: EvaluationsComponent.component.
Which indicates that the problem is with isEvaluatorRequired
, here: [required]="isEvaluatorRequired"
.
I simplified the example to share only the elements around the issue. I want to know why is it giving the error altough it changes the behavior correctly?
I thought the problem was with the binding in [required]
, but, actually the problem was here:
[ngClass]="{'is-invalid': form.submitted && formEvaluator.invalid}"
And it had the wrong behavior as well. Why is is not working?
I solved it by replacing formEvaluator.invalid
with isEvaluatorRequired && !evaluatorId
which is manual validation of the field and it worked. But I still want to know why the formEvaluator.invalid
reference cannot be used after the validation changes.
Upvotes: 0
Views: 108
Reputation: 801
The error message indicates that isEvaluatorRequired
is changed after change detection runs. One way to avoid this is to use a Signal or Subject and the AsyncPipe:
[required]="isEvaluatorRequired$ | async"
Or you could force change detection after updating isEvaluatorRequired
:
// ChangeDetectorRef needs to be injected:
private cdr = inject(ChangeDetectorRef);
// After changing isEvaluatorRequired:
this.cdr.detectChanges();
Upvotes: 2