I'm trying to divide a Material Angular mat-step
(from a mat-horizontal-stepper
) into separate nested sub-forms components, and get a ExpressionChangedAfterItHasBeenCheckedError
regarding the validity of the form.
This stackblitz demonstrates the problem: https://stackblitz.com/edit/mat-stepper-components
The nested components are in step one.
Interestingly enough, the problem doesn't happen if there is just one level of nesting (if steps do not have nested sub-forms), as demonstrated in step two.
Here are the main parts of the code:
create-profile.component.html
<mat-horizontal-stepper [linear]=true #stepper>
<mat-step [stepControl]="frmStepOne">
<ng-template matStepLabel>Step One Details</ng-template>
<form [formGroup]="frmStepOne"><ng-template matStepLabel>Step One</ng-template>
<step-one-component></step-one-component>
</form>
</mat-step>
<mat-step [stepControl]="frmStepTwo">
<ng-template matStepLabel>Step Two Details</ng-template>
<form [formGroup]="frmStepTwo">
<step-two-component></step-two-component>
</form>
</mat-step>
</mat-horizontal-stepper>
create-profile.component.ts
//...
constructor(private fb: FormBuilder) {
this.frmStepOne = new FormGroup({});
this.frmStepTwo = new FormGroup({});
}
step-one.component.html
<step-one-child-one></step-one-child-one>
step-one.component.ts
// nothing interesting, just component boilerplate
step-one-child-one.component.html
<mat-form-field>
<input matInput formControlName="name" placeholder="Name" required>
</mat-form-field>
step-one-child-one.component.ts
@Component({
selector: 'step-one-child-one',
templateUrl: './step-one-child-one.component.html',
viewProviders: [
{
provide: ControlContainer,
useExisting: FormGroupDirective
}
]
})
export class StepOneChildOneComponent {
constructor(private parent: FormGroupDirective) {
}
ngOnInit() {
this.parent.form.addControl('name', new FormControl('', [Validators.required]));
}
}
You need to implement change detection.
Please update create-profile.component.ts with following code
import { Component, ViewChild, Input, ChangeDetectorRef, AfterViewChecked } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { MatStepper } from '@angular/material';
import { StepOneComponent } from './step-one.component';
import { StepTwoComponent } from './step-two.component';
import { StepThreeComponent } from './step-three.component';
@Component({
selector: 'create-profile-component',
templateUrl: './create-profile.component.html'
})
export class CreateProfileComponent implements AfterViewChecked {
frmStepOne: FormGroup;
frmStepTwo: FormGroup;
@ViewChild('StepTwoComponent') stepThreeComponent: StepThreeComponent;
get frmStepThree() {
return this.stepThreeComponent ? this.stepThreeComponent.frmStepThree : null;
}
constructor(private fb: FormBuilder,
private changeDetect: ChangeDetectorRef) {
this.frmStepOne = new FormGroup({});
this.frmStepTwo = new FormGroup({});
}
ngAfterViewChecked(): void {
this.changeDetect.detectChanges();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With