I have a Material Angular Stepper (here a stackblitz running example) and I would like to reset the form on each step when the step is loaded.
I found in the Material Angular docs
that we can use reset()
method on MatStep
to
Resets the step to its initial state. Note that this includes resetting form data.
But, each time I call this method, it generates an error : Error: list is undefined
Here, a stackblitz running example
app.component.html,
<h1>{{ name }}</h1>
<hr />
<mat-stepper
#stepper
(selectionChange)="reloadStep($event)"
>
<mat-step #s1>
<ng-template matStepLabel>Username</ng-template>
<ng-template matStepContent>
<form>
<input type="text" placeholder="Username" />
</form>
</ng-template>
</mat-step>
<mat-step #s2>
<ng-template matStepLabel>Something else </ng-template>
<ng-template matStepContent>
<form>
<input type="text" placeholder="Something else ..." />
</form>
</ng-template>
</mat-step>
</mat-stepper>
app.component.ts,
import { Component, AfterViewInit, ViewChildren} from '@angular/core';
import { MatStepper, MatStep } from '@angular/material/stepper';
import { StepperSelectionEvent } from '@angular/cdk/stepper';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements AfterViewInit {
name = "Material Angular Stepper";
@ViewChildren('stepper') stepper!: MatStepper;
@ViewChildren('s1') one!: MatStep;
@ViewChildren('s2') two!: MatStep;
steps: MatStep[] = [];
ngAfterViewInit(): void {
this.steps = [this.one, this.two];
}
reloadStep = (stepperEvent: StepperSelectionEvent) => {
const index = stepperEvent.selectedIndex;
console.log('step changed:', index);
this.steps[index].reset();
};
}
CodePudding user response:
There are some changes you must make in your code for it to work as you expect.
First of all, if you just want to reset the selected step, you can do it in the reloadStep
directly, without the need for the MatStep
array. This eliminates the list is undefined
error:
reloadStep = (stepper: StepperSelectionEvent) => {
stepper.selectedStep.reset(); // this line calls the reset on the selected step
};
For the form resetting part, that needs some extra work. If you are not familiarized with Reactive Forms, this part may be a little confusing. Start by importing the ReactiveFormsModule
in your AppModule
or the corresponding module:
// ... Other imports
// ...
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
// ... Other imports
// ...
@NgModule({
imports: [
// ...
FormsModule,
ReactiveFormsModule,
// ...
],
// ...
})
export class AppModule {}
Then, in your component.ts
you need to create two FormGroup
instances. I'll just put the relevant parts of the code here:
import { FormBuilder, FormGroup } from '@angular/forms';
// ...
export class AppComponent implements OnInit {
// ...
name = "Material Angular Stepper";
firstForm: FormGroup;
secondForm: FormGroup;
constructor(private formBuilder: FormBuilder) {}
ngOnInit(): void {
// Instantiate the two FormGroups with one control each
this.firstForm = this.formBuilder.group({
username: '',
});
this.secondForm = this.formBuilder.group({
somethingelse: '',
})
}
// ...
reloadStep = (stepper: StepperSelectionEvent) => {
stepper.selectedStep.reset(); // this line calls the reset on the selected step
};
}
Last but not least, in the template you have to specify the [stepControl]
input property of each MatStep
, [formGroup]
input property of each form and formControlName
of each input control to match the name of the control defined in your component.ts
file:
<mat-step #s1 [stepControl]="firstForm">
<ng-template matStepLabel>Username</ng-template>
<ng-template matStepContent>
<form [formGroup]="firstForm">
<input type="text" placeholder="Username" formControlName="username" />
</form>
</ng-template>
</mat-step>
<mat-step #s2 [stepControl]="secondForm">
<ng-template matStepLabel>Something else </ng-template>
<ng-template matStepContent>
<form [formGroup]="secondForm">
<input
type="text"
placeholder="Something else ..."
formControlName="somethingelse"
/>
</form>
</ng-template>
</mat-step>
Only then, each time you select a step, it is reset as well as the corresponding form.
Here's a modified Stackblitz: https://stackblitz.com/edit/angular-ivy-cwkqzx?file=src/app/app.component.html,src/app/app.component.ts,src/app/app.module.ts
CodePudding user response:
If you want to use @ViewChildren your type will always be QueryList like that:
In this example I deleted templateref because you can refer to MatStepper component with it's classname.
@ViewChildren(MatStepper) stepper!: QueryList<MatStepper>;
You should use @ViewChildren when you have multiple components on the template. In this case you have only one MatStepper so you can use @ViewChild.
- We grab MatStepper:
@ViewChild(MatStepper) stepper!: MatStepper;
- Write method that select step by index from stepper
this.stepper.steps.find((_, index) => index === selectedIndex)
- Reset the current step by index:
this.getStepByIndex(stepper.selectedIndex).reset()
Viewchild/Viewchildren documentation: https://angular.io/api/core/ViewChildren
Your modified stackblitz example: