Home > database >  How to reset a 'Material Angular Stepper' Step?
How to reset a 'Material Angular Stepper' Step?

Time:10-20

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.

  1. We grab MatStepper:

@ViewChild(MatStepper) stepper!: MatStepper;

  1. Write method that select step by index from stepper

this.stepper.steps.find((_, index) => index === selectedIndex)

  1. Reset the current step by index:

this.getStepByIndex(stepper.selectedIndex).reset()

Viewchild/Viewchildren documentation: https://angular.io/api/core/ViewChildren

Your modified stackblitz example:

https://stackblitz.com/edit/angular-ivy-dnh7qb?file=src/app/app.component.ts,src/app/app.component.html

  • Related