Reputation: 1261
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
Upvotes: 15
Views: 22824
Reputation: 91
To have a mat-stepper with each step as its own component, create the buttons to traverse through the stepper outside the component and show/hide the traversal buttons based on form validation done inside the individual component and expose the form info to the parent stepper.
For Example:
<mat-horizontal-stepper #stepper linear iseditable>
<mat-step
[stepControl]="firstFormGroup"
[completed]="primaryIsTrue"
>
<app-primary-settings
(formIsValid)="formValidity($event)"></app-primary-settings>
<button mat-button matStepperNext
*ngIf="primaryIsTrue">
Next
</button>
</mat-step>
</mat-horizontal-stepper>
Upvotes: 0
Reputation: 2823
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]
});
}
}
Upvotes: 29
Reputation: 2423
Your stepper and forms components works on different form objects. You need to set super's forms objects in step component's ngOnInit()
ngOnInit() {
super.frmStepTwo = this.formBuilder.group({
address: ['', Validators.required]
});
}
instead
ngOnInit() {
this.frmStepTwo = this.formBuilder.group({
address: ['', Validators.required]
});
}
Upvotes: 0