Reputation: 911
I have angular reactive form in parent component and sections inside childrens component.
Inside the child component I have a checkbox - when its checked - more fields open and I want them all to be required.
I am using setValidators but I'm getting error
ParentFormComponent.html:3 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ng-valid: true'. Current value: 'ng-valid: false'. at viewDebugError (core.js:7601) at expressionChangedAfterItHasBeenCheckedError (core.js:7589) at checkBindingNoChanges (core.js:7691) at checkNoChangesNodeInline (core.js:10560) at checkNoChangesNode (core.js:10541) at debugCheckNoChangesNode (core.js:11144) at debugCheckRenderNodeFn (core.js:11098) at Object.eval [as updateRenderer] (ParentFormComponent.html:3) at Object.debugUpdateRenderer [as updateRenderer] (core.js:11087) at checkNoChangesView (core.js:10442)
ParentFormComponent.html:3 ERROR CONTEXT DebugContext_ {view: Object, nodeIndex: 2, nodeDef: Object, elDef: Object, elView: Object}
this is the line of ParentFormComponent.html:3
<form [formGroup]="parentForm" (ngSubmit)="submitForm()">
Here is my code:
<label class="container">DB
<input #db type="checkbox" name="db" (change)="checkValue(db.name, db.checked)">
<span class="checkmark"></span>
</label>
<div *ngIf="db.checked" formGroupName="_monitorDB">
<mat-form-field>
<input matInput placeholder="Server name" formControlName="ServerName">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="DataBase name" formControlName="DbName">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="table name" formControlName="DB_tableName">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="port" formControlName="DbPort">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Query" formControlName="Query">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Permissions" formControlName="Premissions">
</mat-form-field>
</div>
and in the ts file:
checkValue(name:string, event: any){
if (event == true){
this.test.push(name);
if (name =="db"){
this.childForm.get('_monitorDB').get('ServerName').setValidators([Validators.required]);
this.childForm.get('_monitorDB').get('DbName').setValidators([Validators.required]);
this.childForm.get('_monitorDB').get('DB_tableName').setValidators([Validators.required]);
this.childForm.get('_monitorDB').get('DbPort').setValidators([Validators.required]);
this.childForm.get('_monitorDB').get('Query').setValidators([Validators.required]);
this.childForm.get('_monitorDB').get('Premissions').setValidators([Validators.required]);
}
}
else{
const index: number = this.test.indexOf(name);
if (index !== -1) {
this.test.splice(index, 1);
if (name =="db"){
this.childForm.get('_monitorDB').get('ServerName').clearValidators();
this.childForm.get('_monitorDB').get('ServerName').updateValueAndValidity();
this.childForm.get('_monitorDB').get('DbName').clearValidators();
this.childForm.get('_monitorDB').get('DbName').updateValueAndValidity();
this.childForm.get('_monitorDB').get('DB_tableName').clearValidators();
this.childForm.get('_monitorDB').get('DB_tableName').updateValueAndValidity();
this.childForm.get('_monitorDB').get('DbPort').clearValidators();
this.childForm.get('_monitorDB').get('DbPort').updateValueAndValidity();
this.childForm.get('_monitorDB').get('Query').clearValidators();
this.childForm.get('_monitorDB').get('Query').updateValueAndValidity();
this.childForm.get('_monitorDB').get('Premissions').clearValidators();
this.childForm.get('_monitorDB').get('Premissions').updateValueAndValidity();
}
}
}
this.checkboxArr.emit(this.test);
}
Upvotes: 46
Views: 77842
Reputation: 1340
I tend to never use setTimeout
as I think it's a bad practice, but I have lately found that when creating custom components and interacting with the DOM, this is sometimes necessary.
However, when I've used it, it has been with a delay of 0ms
. Just wrapping the action in a setTimeout
with no delay will be enough to move the action to the end of the execution queue, which will solve numerous issues with the interaction between Angular and the DOM.
setTimeout(() => actionThatCausesTheError(), 0);
Upvotes: 1
Reputation: 2572
Track where the field is changed and mark the component for changeDetection... it could be in a function or one of Angular lifecycle hooks like AfterViewChecked
constructor(
private _cdr: ChangeDetectorRef
) {
}
// mark for check where change is happening
this._cdr.markForCheck();
this._cdr.detectChanges();
Changing changeDetection to ChangeDetectionStrategy.OnPush will also solve the problem but it might cause other problems, you will need to manually mark the component for check.
Upvotes: 1
Reputation: 364
Change the ChangeDetectionStrategy on the parent component to : changeDetection: ChangeDetectionStrategy.OnPush
. Simple and clean.
Edit This actually not a solution, it will just hide the error. Please take a look at the github link posted in the comments.
Upvotes: 11
Reputation: 3899
I faced the same issue and I fixed it by using AfterViewChecked
and ChangeDetectorRef
:
import { AfterViewChecked, ChangeDetectorRef } from '@angular/core'
export class ClassName implements AfterViewChecked {
constructor(private readonly changeDetectorRef: ChangeDetectorRef) {}
ngAfterViewChecked(): void {
this.changeDetectorRef.detectChanges();
}
}
Upvotes: 65
Reputation: 17078
Adding changeDetection: ChangeDetectionStrategy.OnPush
should do a trick
@Component({
selector: 'app-test-center-location-step3',
templateUrl: './test-center-location-step3.component.html',
styleUrls: ['./test-center-location-step3.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
Upvotes: 2
Reputation: 878
try this one:
ngOnInit():void
{
this._monitorDB.patchValue({
ServerName: 'ServerName',
DbName: 'DbName',
DB_tableName: 'DB_tableName',
DbPort: 'DbPort',
Query: 'Query',
Premissions:'Premissions'
});
}
Upvotes: 0
Reputation: 1366
I am facing same issue I know it's not a way but the only solution I've found is setTimeout
ngAfterViewInit(): void {
setTimeout(() => {
this.form.patchValue({
name: 'xyz',
email: '[email protected]',
phone: '0987654321'
})
}, );
}
Upvotes: 7
Reputation: 911
I don't know if it's the right thing to do but i solved it by changing:
<div *ngIf="db.checked" formGroupName="_monitorDB">
to
<div [hidden]="!db.checked" formGroupName="_monitorDB">
Upvotes: 3