I have a angular material linear stepper
each step is a separate angular component
containing a form
which needs validation
The validation simply just isn't working. I can progress through to the next step without completing the form.
To illustrate what I mean I have created a condensed version on stackblitz.
The main things to look at (I think) is the create-profile.component.html
<mat-horizontal-stepper linear #stepper>
<mat-step [stepControl]="frmStepOne">
<ng-template matStepLabel>Step One Details</ng-template>
<step-one-component></step-one-component>
</mat-step>
<mat-step [stepControl]="frmStepTwo">
<ng-template matStepLabel>Step Two Details</ng-template>
<step-two-component></step-two-component>
</mat-step>
<mat-step [stepControl]="frmStepThree">
<ng-template matStepLabel>Step Three Details</ng-template>
<step-three-component></step-three-component>
</mat-step>
</mat-horizontal-stepper>
And each step-X-component
Here is the stackblitz. https://stackblitz.com/edit/angular-vpoj5j
For each mat-step , the stepControl attribute can be set to the top level AbstractControl that is used to check the validity of the step. There are two possible approaches. One is using a single form for stepper, and the other is using a different form for each step.
There are three main types of stepper motors: Permanent magnet stepper. Variable reluctance stepper. Hybrid synchronous stepper.
The problem is in your CreateProfileComponent
:
@Component({
selector: 'create-profile-component',
templateUrl: './create-profile.component.html'
})
export class CreateProfileComponent {
frmStepOne: FormGroup;
frmStepTwo: FormGroup;
frmStepThree: FormGroup;
constructor(private fb: FormBuilder) { }
}
There is no relation between your defined FormGroups in CreateProfileComponent
and your stepper components. You tried to extend every StepComponent
with CreateProfileComponent
, but with this approach every StepComponent
has its own instance of CreateProfileComponent
and so their own FormGroup
declaration.
To solve your problem you can declare template variables for every StepComponent
in your html (starting with #
) and pass the formControl to [stepControl]
:
<mat-horizontal-stepper linear #stepper>
<mat-step [stepControl]="step1.frmStepOne">
<ng-template matStepLabel>Step One Details</ng-template>
<step-one-component #step1></step-one-component>
</mat-step>
<mat-step [stepControl]="step2.frmStepTwo">
<ng-template matStepLabel>Step Two Details</ng-template>
<step-two-component #step2></step-two-component>
</mat-step>
<mat-step [stepControl]="step3.frmStepThree">
<ng-template matStepLabel>Step Three Details</ng-template>
<step-three-component #step3></step-three-component>
</mat-step>
</mat-horizontal-stepper>
Or you leave your html as it is and work with ViewChild()
(my preferred approach):
@Component({
selector: 'create-profile-component',
templateUrl: './create-profile.component.html'
})
export class CreateProfileComponent {
@ViewChild(StepOneComponent) stepOneComponent: StepOneComponent;
@ViewChild(StepTwoComponent) stepTwoComponent: StepTwoComponent;
@ViewChild(StepTwoComponent) stepThreeComponent: StepThreeComponent;
get frmStepOne() {
return this.stepOneComponent ? this.stepOneComponent.frmStepOne : null;
}
get frmStepTwo() {
return this.stepTwoComponent ? this.stepTwoComponent.frmStepTwo : null;
}
get frmStepThree() {
return this.stepThreeComponent ? this.stepThreeComponent.frmStepThree : null;
}
}
Either way there is no need to extend your StepComponents
with CreateProfileComponent
and it doesn't make any sense.
@Component({
selector: 'step-x-component',
templateUrl: './step-x.component.html',
})
export class StepXComponent {
public frmStepX: FormGroup;
constructor(private formBuilder: FormBuilder) {
}
ngOnInit() {
this.frmStepX = this.formBuilder.group({
name: ['', Validators.required]
});
}
}
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