angular material stepper: disable header navigation
AngularAngular MaterialAngular Problem Overview
I want to navigate the stepper only through the next and back buttons.
I can't get this to work since users can also click each step label to navigate to any step. I can't use linear, since it requires each step to have a formArray
or FormGroup
.
I have tried <mat-step (click)="$event.stopPropagation()">
.
Angular Solutions
Solution 1 - Angular
Add this to your style sheet. I was trying to disable the header navigation. Tried many things but this hack worked. You can try this till Angular Material Team support this feature.
::ng-deep .mat-horizontal-stepper-header{
pointer-events: none !important;
}
Solution 2 - Angular
Use a linear
stepper with completed=false
steps. When the user presses your button, programattically complete the step and move to the next one.
This way you don't need to mess with CSS pointer events. In our app, that resulted in accessibility problems with NVDA.
<mat-horizontal-stepper linear #stepper>
<mat-step completed="false">
<ng-template matStepLabel>Step 1</ng-template>
<app-some-child (nextClicked)="nextClicked($event)" ></app-some-child>
</mat-step>
<mat-step>
<ng-template matStepLabel>Step 2</ng-template>
<app-some-other-child></app-some-other-child>
</mat-step>
</mat-horizontal-stepper>
export class AppComponent implements OnInit {
@ViewChild('stepper') stepper: MatStepper;
nextClicked(event) {
// complete the current step
this.stepper.selected.completed = true;
// move to next step
this.stepper.next();
}
}
Solution 3 - Angular
Don't use ::ng-deep as it is deprecated.
https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep
Instead, if you are using Angular Material, use the theme guide from the documentation.
https://material.angular.io/guide/theming
Example of a implementation of the style:
my-custom-elements.scss
@import '~@angular/material/theming';
@mixin custom-stepper-theme() {
.mat-horizontal-stepper-header {
pointer-events: none;
}
}
global-material-theme.scss
@import '~@angular/material/theming';
// Plus imports for other components in your app.
// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat-core();
@import './material/my-custom-elements.scss';
@include custom-stepper-theme();
angular.json
...
"styles": ["src/styles.scss", "src/app/global-material-theme.scss"]
...
Solution 4 - Angular
Does not work without ::ng-deep
::ng-deep .mat-horizontal-stepper-header{
pointer-events: none !important;
}
Solution 5 - Angular
For anyone that's still looking for an alternate solution if ::ng-deep
does not work.
Furthermore, ::ng-deep
is deprecated and setting ViewEncapsulation
to none is the preferred way to go if you want to do it using CSS.
Import ViewEncapsulation
and set to None
in your
compnent.ts:
import { Component, OnInit, ViewEncapsulation } from "@angular/core";
@Component({
selector: "stepper-overview-example",
templateUrl: "stepper-overview-example.html",
styleUrls: ["stepper-overview-example.css"],
encapsulation: ViewEncapsulation.None
})
export class StepperOverviewExample implements OnInit {
isLinear = false;
constructor() {}
ngOnInit() {}
}
set pointer-events
to none
in your
component.css:
.mat-horizontal-stepper-header {
pointer-events: none !important;
}
Here's a DEMO.
Solution 6 - Angular
Just to improve on the accepted answer, as it will not work if you have a vertical stepper.
To stop the user being able to click the header and navigate add the following code to you style.css file in the root:-
.mat-step-header {
pointer-events: none !important;
}
This will ensure it works for mat-horizontal-stepper-header
and mat-vertical-stepper-header
Solution 7 - Angular
I found a somewhat hacky solution for this. The problem is that you cannot fully disable the header navigation, but you can prevent it from being active until a certain moment.
And that moment is form.invalid
.
My situation was the following: User needs to fill the form inside the step, press 'save' and only then be able to use NEXT
button and navigate the stepper further (also back).
What I did was introduce another hidden
input
which would be using angular's [required]
dynamic attribute. It will only be required
if the previous save
condition wasn't successful. Once it succeeds this field won't be required
and user can navigate further.
Together with mat-stepper (or md-stepper) attribute editable
you should be able to achieve what you want.
Let me know if you fully understood the idea.
Solution 8 - Angular
Here ::ng-deep .mat-horizontal-stepper-header-container { display: none ; }
Use this on your style sheet to remove stepper header...Like Step-1,Step-2
Solution 9 - Angular
First you need add ViewEncapsulation.None
in your component config
@Component({
selector: 'app-example',
encapsulation: `ViewEncapsulation.None`
})
Then add this in your component CSS.
.mat-horizontal-stepper-header-container {
display: none !important;
}
Solution 10 - Angular
This also did the trick for me
Requirement:
- Allow navigation either from Navigation or Next Button only WHEN
form
is valid - Disabled Header navigation and keep button disabled till form is not valid.
<mat-horizontal-stepper #stepper [linear]="true">
<!----------------------------->
<!---- STEP: 1:---->
<!----------------------------->
<mat-step #generalStep [completed]="formGroup1.valid">
<ng-template matStepLabel>Step-1</ng-template>
<form [formGroup]="formGroup1">
// STEP-1 Content
// matInput - with form control bindings
<div class="container">
<button mat-raised-button matStepperNext color="primary"[disabled]="!formGroup1.valid">Next Step</button>
</div>
</form>
</mat-step>
<!------------------------------->
<!-- STEP: 2:-->
<!------------------------------->
<mat-step #generalStep [completed]="formGroup2.valid">
<ng-template matStepLabel>Step-2</ng-template>
<form [formGroup]="formGroup2">
// STEP-2 Content
// matInput - with form control bindings
<button mat-raised-button matStepperNext color="primary"[disabled]="!formGroup.valid">Next Step</button>
</form>
</mat-step>
</mat-horizontal-stepper>
- FormGroup/FormControl initialization
- Adding Validators
formGroup1: FormGroup;
formGroup2: FormGroup;
this.formGroup1 = this.formBuilder.group({
control1: new FormControl('', [Validators.required]),
control2: new FormControl('', [Validators.required]),
});
this.formGroup1 = this.formBuilder.group({
control3: new FormControl('', [Validators.required]),
control4: new FormControl('', [Validators.required]),
});
Solution 11 - Angular
You need to add an "linear" attribute (This will disable navigation)
<mat-vertical-stepper linear>
Solution 12 - Angular
In the
<mat-horizontal-stepper labelPostion="botton" [linear]="true">
...
</mat-horizontal-stepper>