Angular Material Stepper component prevent going to all the non visited steps
Asked Answered
C

4

15

I'm using the Angular Material Stepper component.

Within my content I have separate buttons that helps the user to move to the next step once the task in the current step is complete.

I want to prevent the user from visiting the next steps by clicking the step buttons of the stepper component.

However, I want the user to be able to go back to a previous step via the step buttons of the stepper component.

I'm not using form inside the stepper. I've seen the Linear property of the component, but it does not suit my requirement.

In brief, prevent the user from going to the "unvisited" steps by clicking the step buttons of the stepper component.

Chura answered 23/10, 2018 at 11:11 Comment(3)
Check out Liner Stepper in Angular Material docsCarry
No, not my requirement. I want to "prevent the user from visiting the next steps by clicking the step buttons of the stepper component". User should not be able to go to next steps but should be able to go to previous step.Lovelorn
Is the User at some point able to go to the next step by clicking the next step button?Venettavenezia
C
42

The solution that I found to this problem is to use completed attribute of step. Refer to the line of code given below:

<mat-step [completed]="isCompleted">

When isCompleted is true it will enable the next step.

Note: For this to work, the stepper component must be in the linear mode. This can be done by setting the attribute linear on the stepper component, like

<mat-horizontal-stepper linear>

Chura answered 24/10, 2018 at 10:2 Comment(1)
Yes it will work without form. You can find more information in this github linkCiaphus
C
3

Check this link . You need to use linear stepper.

A stepper marked as linear requires the user to complete previous steps before proceeding. For each step, the stepControl attribute can be set to the top level AbstractControl that is used to check the validity of the step.

Example shown as below

    import { Component, Input } from '@angular/core';
    import {FormBuilder, FormGroup, Validators} from '@angular/forms';
    import {MatIconRegistry} from '@angular/material';
    
    @Component({
      selector: 'stepper',
      templateUrl: './stepper.component.html'
    })
        export class StepperComponent  {
           isLinear = true;
          firstFormGroup: FormGroup;
          secondFormGroup: FormGroup;
        
          constructor(private _formBuilder: FormBuilder){
        
          }
           ngOnInit() {
            this.firstFormGroup = this._formBuilder.group({
              firstCtrl: ['', Validators.required]
            });
            this.secondFormGroup = this._formBuilder.group({
              secondCtrl: ['', Validators.required]
            });
          }
        }

and html is

    <mat-vertical-stepper [linear]="isLinear">
      <mat-step [stepControl]="firstFormGroup">
        <form [formGroup]="firstFormGroup">
          <ng-template matStepLabel>Fill out your name</ng-template>
          <mat-form-field>
            <input matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
          </mat-form-field>
          <div>
            <button mat-button mat-raised-button color="primary" matStepperNext>Next</button>
          </div>
        </form>
      </mat-step>
      <mat-step [stepControl]="secondFormGroup">
        <form [formGroup]="secondFormGroup">
          <ng-template matStepLabel>Fill out your address</ng-template>
          <mat-form-field>
            <input matInput placeholder="Address" formControlName="secondCtrl" required>
          </mat-form-field>
          <div>
            <button mat-button mat-raised-button color="primary" matStepperPrevious>Back</button>
            <button mat-button mat-raised-button color="primary" matStepperNext>Next</button>
          </div>
        </form>
      </mat-step>
      <mat-step>
        <ng-template matStepLabel icon>Done</ng-template>
        You are now done.
        <div>
          <button mat-button mat-raised-button color="primary" matStepperPrevious>Back</button>
        </div>
      </mat-step>
    </mat-vertical-stepper>
Ciaphus answered 23/10, 2018 at 11:36 Comment(3)
No, not my requirement. I want to "prevent the user from visiting the next steps by clicking the step buttons of the stepper component". User should not be able to go to next steps but should be able to go to previous step.Lovelorn
@TempO'rary it is your requirement, you just need to understand it. This is how it is done. Using next button will not work unless form is valid. Going back will work.Asch
I get it, but Im not using form. Then my question would be, how to do it when not using form?Lovelorn
B
1

I had this issue and trying to solve it i've stumbled upon this post. My goal was to block the next step (formGroup) before the users at least had some input on the fields. So I came up with this simple solution:

HTML (notice I have a mix between ngBoostrap and Angular Material, no harm done, I hope :)

<div class="container-fluid ">
    <div class="row" style="height: 100vh">
        <div class="col-md-6">
               
            <mat-vertical-stepper [linear]="namesGroup.invalid" #stepper>
                <mat-step [stepControl]="namesGroup" >
                    <form [formGroup]="namesGroup">
                        <ng-template matStepLabel>Fill out your name and username</ng-template>
                        <mat-form-field>
                            <mat-label>Name</mat-label>
                            <input matInput placeholder="First name" formControlName="name" required>
                        </mat-form-field>
                        <!--Errors-->
                        <div *ngIf="name.touched && name.invalid">
                            <mat-error *ngIf="name.errors.required"><small>Name is required</small></mat-error>
                        </div>
                        <!--Errors-->
                        <mat-form-field>
                            <mat-label>Username</mat-label>
                            <input matInput placeholder="Username" formControlName="username" required>
                        </mat-form-field>
                        <!--Errors-->
                        <div *ngIf="username.touched && username.invalid">
                            <mat-error *ngIf="username.errors.required"><small> Username is required</small></mat-error>
                        </div>
                        <!--Errors-->
                        <div>
                            <button [disabled]="namesGroup.invalid" mat-raised-button color="primary" matStepperNext>Next</button>
                        </div>
                    </form>

I have two more form groups and the logic repeats itself. So im just binding the [linear] atribute to the valid/invalid state of the form. And it works like a charm. I hope I have contributed. Cheers

Belayneh answered 22/6, 2020 at 22:6 Comment(1)
That's a very nice solution! Simply making it linear is not what I wanted, because you would have to mess with Stepper's internal validation mechanism. With your solution I can now control it manually.Bridie
S
0

you can add any condition to the button to disable and enable only when it meets the condition..best option

 <button  [disabled]="this.var == 'yes' 
   && this.var2 == 'Select File'" 
   type="button" 
   matStepperNext>

           Next Step

  </button>
Schermerhorn answered 18/7, 2023 at 6:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.