Angular Material Stepper causes mat-formfield to validate for dynamic forms when returning to an older step
Background Information
The nicest solution I have found is changing a parameter on the mat-stepper
. There is a step selected at any given time. On step you can change whether the step has been interacted with or not. If a step has been previously visited the interacted
parameter will then be set to true. As this is intended and makes sense it will however cause a problems adding a class to all the mat-form-fields
causing them to go red.
Scenarios where this is causes a bad user experience:
You complete the first step and go to the second step. After going to the second step you realize you made a mistake on the first step and decide to navigate back to the first step. You make your changes and now proceed once again to the second step. Because you have already visited this step if you have
mat-form-fields
a class will be added (possibly other changes) and your form fields are now all red. This is a poor user experience as the user has technically not made any mistakes and can be over bearing.In the first step you have it where you are creating dynamic forms. For simplicity let use the tour of heroes analogy. In our first step you can dynamically add forms. Each form represents a new hero you want to add. You have added 3 heroes and moved on to step 2. Before completing step 2 you realize you forgot a few heroes and proceed back to step 1. Now when you click our button 'create hero' your dynamic form pops up but all your
mat-form-fields
are now red like the user made a mistake. This is another poor user experience.
The Fix:
hero.stepper.html
<mat-horizontal-stepper [linear]="true"
(selectionChange)="stepChanged($event, stepper);">
<mat-step [stepControl]="hiddenForm" label="step 1"
<form [formGroup]="heroFormGroupArray[0]">
<mat-form-field>
<mat-label>Hero Name</mat-label>
<input [formControl]="heroName"matInput>
...
</mat-form-field>
</form>
<button (click)="AddNewHero()">Add New Hero</button>
</mat-step>
</mat-horizontal-stepper>
hero.stepper.ts
export class heroStepComponent implements OnInit {
constructor(){
...
}
ngOnInit(){
...
}
stepChanged(event, stepper){
stepper.selected.interacted = false;
}
}
Following on previous post I've found better implementation for stepChanged function:
stepChanged(event: StepperSelectionEvent) {
if (event.previouslySelectedIndex > event.selectedIndex) {
event.previouslySelectedStep.interacted = false;
}
}
This code will set interacted property only on steps which you go from to previous steps.