Nandini S
Nandini S

Reputation: 209

disable mat-step in mat-vertical-stepper

I have 4 mat-step in mat-vertical-stepper. I want to disable 2nd,3rd & 4th mat-step until the 1st mat-step's all fields covered.

I tried:

<mat-step label="Step 1">
    <!-- some codes-->
</mat-step>

In Step 1 I have a next button and this button is disabled till all fields are covered.

<button mat-raised-button color="primary" style="float:right"
     [disabled]="!DetailsForm.valid" (click)="step2.disabled = false">Next</button> 

Next is STEP 2:

<mat-step label="Step 2" [disabled]="step2.disabled">

it shows an error "disabled is not a part of mat-step".

Like this, rest two mat-step are there. I want to disable 2nd,3rd,4th mat-step.

In below case, how can I use linear?

    <mat-vertical-stepper #stepper>
       <mat-step label="General Details">
           <h4> First Name </h4>
       </mat-step>
       <mat-step label="Education">
           <h4>Highest Education </h4>
       </mat-step>
    </mat-vertical-stepper>

And,

   <div class="col-md-9 col-lg-9">
     <form [formGroup]="generalDetailsForm">
       <div class="row">
         <div class="col-md-5 col-lg-5">
           <mat-form-field class="example-full-width">
             <input matInput placeholder="First Name" [(ngModel)]="firstName">
           </mat-form-field>
         </div>
      </div>
    </form>
   </div>

Upvotes: 13

Views: 32209

Answers (11)

Bruno Oliveira
Bruno Oliveira

Reputation: 39

Use a FormGroup just to allow next step.

Example: .ts

    this.clientForm = this.formBuilder.group({
          client: [this.client, Validators.required],
        });
    this.clientConfigForm = this.formBuilder.group({
          formOk: ['', Validators.required],
          clientForm: this.clientForm
        });

.html

 <mat-stepper orientation="vertical" linear>

    <mat-step [stepControl]="clientConfigForm">
      <form [formGroup]="clientForm" (ngSubmit)="clientConfigForm.controls.formOk.setValue('ok')">
        <mat-form-field appearance="outline">
          <input type="text" matInput maxlength="10" formControlName="client" class="form-control">
        </mat-form-field>
        
          <button mat-raised-button color="primary"
            [disabled]="!clientForm.valid">
            Next
          </button>

      </form>
    </mat-step>

    <mat-step>
      ...
    </mat-step>

Upvotes: 0

Gy&#246;rgy Szy
Gy&#246;rgy Szy

Reputation: 31

Using @Hypenate's answer, I just wanted to force the user to stay at the current step while the formGroup (connedted to the current step) is INVALID.

So I made a css definition in styles.css:

.mat-element-notclickable{
    pointer-events: none;
    cursor: not-allowed;
    opacity: 0.5;
    color: #cccccc;
}

than in ngOnInit I just subscribe to the formGroup's statusChange and disable all the steps as needed:

    ngOnInit() {
        this.formGroup.statusChanges.subscribe( newStatus => {
            if( newStatus === 'VALID') {
                Array.from(document.getElementsByClassName('mat-step-header')).forEach(element => {
                    element.classList.remove('mat-element-notclickable')
                });
            } else {
                Array.from(document.getElementsByClassName('mat-step-header')).forEach(element => {
                    element.classList.add('mat-element-notclickable')
                })
            }
        })
    }

(of course, you could just use the header container, and use only one .classList.add and remove, but in other places I will need to enable/disable steps separately)

Upvotes: 0

Hypenate
Hypenate

Reputation: 2064

I had to disable a step depending on a condition.
I created a directive and queried the dom to add a class.

stylesheet:

.mat-step-disabled {
  pointer-events: none;
  opacity: 0.5;
  color: #cccccc;
}

Directive

@Directive({
  selector: '[step-disable]'
})
export class StepDisableDirective implements OnChanges {
  @Input() public isDisabled: boolean = true;

 public ngOnChanges(changes: SimpleChanges): void {
   this.isDisabled? this.disable() : this.enable();
 }
   
  private enable(): void {
    const el:HTMLElement  = this.getElement();
    el.classList.add('mat-step-disabled');
  }

  private disable(): void {
    const el:HTMLElement  = this.getElement();
    el.classList.remove('mat-step-disabled');
  }

  private getElement(): HTMLElement {
    const elements: HTMLCollectionOf<Element> = document.getElementsByClassName(mat-step-header);
    const matStepHeader = elements[0] as HTMLElement;    // Take the first step, you want to pass your index via an Input parameter
    return matStepHeader;
  }    
}

Module

@NgModule({
  declarations: [
    StepDisableDirective 
  ]
})
export class AppModule{ }

HTML
Set the directive and it's input parameter

<mat-step step-disable [isDisabled]="true" label="Foo">

Upvotes: 4

j0131n
j0131n

Reputation: 63

The solution from @delpo and @Matvii should fit your needs.

<mat-vertical-stepper #stepper [linear]="true">
 <mat-step
   state="first"
   [completed]="formGroup.valid"
   [editable]="true">
 </mat-step>
 <mat-step state="final">
 </mat-step>
</mat-vertical-stepper>

This can be achieved by using [linear]="true" and disabling the next step with [completed]="formGroup.valid" by passing the validity of your FormGroup, so whenever the FormGroup is Valid next step should be enabled/should be able to proceed.

Upvotes: 6

SHUBHASIS MAHATA
SHUBHASIS MAHATA

Reputation: 940

Step-1: Component.ts

stepDisabled: boolean = true;

step-2: Component.css

::ng-deep .mat-step-header[aria-labelledby="disabled_Resi"] {
  pointer-events: none !important;
  cursor: not-allowed;
}

step-3: Component.html

  <mat-step [aria-labelledby]="stepDisabled ? 'disabled_Resi' : null" [stepControl]="ResidentalAddressFG"></mat-step>

step:4

<button mat-stroked-button (click)="stepDisabled = !stepDisabled">{{stepDisabled ? 'Enable' : 'Disable'}} Step</button>

Upvotes: 4

Bhavik patel
Bhavik patel

Reputation: 161

<mat-horizontal-stepper #stepper [linear]="true">
<mat-step [completed]="false">
   <!-- set completed = false to disable next step -->
</mat-step>
<mat-step>
   this step would be disable         
</mat-step>
</mat-horizontal-stepper>

Upvotes: 14

Matvii Stelmakh
Matvii Stelmakh

Reputation: 61

if you want to disable step 2 you should use [completed] on step 1, at the same time setting [stepControl] to null, because [stepControl] takes precedence over [completed]

 <mat-horizontal-stepper #stepper [linear]="true">
   <!-- step1 -->
   <mat-step
     [stepControl]="shouldNextStepBeDisabled ? null : formGroup"
     [completed]="shouldNextStepBeDisabled ? false : formGroup.valid">
   </mat-step>
   ....
 </mat-horizontal-stepper>

Upvotes: 4

delpo
delpo

Reputation: 239

For anyone seeing this.

I resolved this with linear completed attributes.

 <mat-horizontal-stepper #stepper [linear]="true">
   <mat-step state="state_1" label="label" [completed]="condition">
   </mat-step>
   ....
 </mat-horizontal-stepper>

Use the completed attribute to mark the step as 'completed'

From the docs:

Alternatively, if you don't want to use the Angular forms, you can pass in the completed property to each of the steps which won't allow the user to continue until it becomes true. Note that if both completed and stepControl are set, the stepControl will take precedence.

https://material.angular.io/components/stepper/overview

Edit: added example

Upvotes: 1

Thanuja
Thanuja

Reputation: 1

Adding attribute class="mat-elevation-z1" worked for me

Upvotes: -7

Nandini S
Nandini S

Reputation: 209

in mat-step use [stepControl]="formName" and in .ts do validation of the form.

Using only linear won't help. I was doing wrong. I did not use [stepControl]

Upvotes: 2

Daniel
Daniel

Reputation: 9839

mat-vertical-stepper has no property disabled as the exception message says.

try setting <mat-vertical-stepper [linear]="true">

after that you need to handle the visibilty of the button as you did. a button has the disabled property.

Upvotes: 1

Related Questions