How to disable the remaining steps in Angular mat stepper

3.6k Views Asked by At

I am creating an angular application which has mat stepper with 4 steps. User has to complete the steps one by one. Only if he completes the form in first step he can go to next step by clicking on the button in the form not by clicking on stepper label. After completing the steps he can still visit all the completed steps by clicking on the stepper label . I know this can be achieved with linear and completed attributes and lot of examples are available .

Scenario is if the user completed till step 4 and he goes back to step 2 , change anything in the form of second step then step 3 and step 4 has to be disabled (even though it is completed it has to become disabled and non editable and user has to navigate via the buttons only like in the beginning). Is it possible to achieve this in mat stepper ?

2

There are 2 best solutions below

0
On

Not sure it's exact answer you want but give it a try.

You can get index of every mat step header and apply logic according to it.

component.html

<mat-horizontal-stepper linear="true" #stepper (selectionChange)="setIndex($event)" (click)="triggerClick()">

component.ts

setIndex(event) {
  this.selectedIndex = event.selectedIndex;
}

triggerClick() {
  console.log(`Selected tab index: ${this.selectedIndex}`);
}

or

You can just prevent user by clicking on label so user can't go to steps via header

In component.ts

import { ViewEncapsulation } from '@angular/core';
@Component({
   .......
   encapsulation: ViewEncapsulation.None, //add this line
})

In css

.mat-horizontal-stepper-header { 
  pointer-events: none !important; 
}

Here is the example of both ideas

stackblitz

2
On

Take a look at the first example in this page: https://material.angular.io/components/stepper/examples

Here, they are using an attribute called editable which can be set to true or false.

My suggestion is, use this attribute, whenever the user comes back to the step1 (for eg, from step 4), then check for form-change. if there is a form-change in that page, make editable to false.

2 Cases here:

1) A single component rendering child components
**************************
<mat-horizontal-stepper linear #stepper>
  <mat-step [stepControl]="firstFormGroup" [editable]="isEditable">
      <child-component-1></child-component-1>
  </mat-step>
  <mat-step [stepControl]="firstFormGroup" [editable]="isEditable">
      <child-component-2></child-component-2>
  </mat-step>
<mat-horizontal-stepper>

If this is the case, what you can do is, use an @Output event emitter from each form and emit a value stating that there is a formchange. If that emitted value is present, then make the [editable] to false;

Something like this: 

@Output() formChanged: EventEmitter = new EventEmitter<boolean>();

if (form1.invalid) {
  this.formChanged.emit(false);
}
*****************************
2) Condition 2: If you have forms directly under your <mat-step> simply set the value of [editable] in the same component. 

<mat-horizontal-stepper linear #stepper>
  <mat-step [stepControl]="firstFormGroup" [editable]="isEditable">
    <form-1>      
    </form-1>
  </mat-step>
  <mat-step [stepControl]="secondFormGroup" [editable]="isEditable">
    <form-2>
    </form-2>
  </mat-step>
  
</mat-horizontal-stepper>


Something like, 

if (form1.invalid) { 
   this.isEditable = false;
}