Angular content stepper provides a wizard-like workflow by dividing the content into logical steps.
The material stepper is responsible for logic on the CDK stepper’s foundation that drives the stepped workflow. The material stepper extends the CDK stepper and a material design style.
Stepper Variants
There are two stepper components: mat-horizontal-stepper and mat-vertical-stepper. They both are used as same way. The only difference is the orientation of the stepper.
app.component.html
<button mat-raised-button (click)="isLinear = !isLinear" id="toggle-linear">
{{!isLinear ? 'Enable linear mode' : 'Disable linear mode'}}
</button>
<mat-horizontal-stepper [linear]="isLinear" #stepper>
<mat-step [stepControl]="firstFormGroup">
<form [formGroup]="firstFormGroup">
<ng-template matStepLabel>Fill out your name</ng-template>
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
</mat-form-field>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step [stepControl]="secondFormGroup" label="Fill out your address">
<form [formGroup]="secondFormGroup">
<mat-form-field>
<mat-label>Address</mat-label>
<input matInput formControlName="secondCtrl" placeholder="Ex. 1 Main St, New York, NY"
required>
</mat-form-field>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step>
<ng-template matStepLabel>Done</ng-template>
<p>You are now done.</p>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button (click)="stepper.reset()">Reset</button>
</div>
</mat-step>
</mat-horizontal-stepper>
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
/**
* @title Stepper overview
*/
@Component({
selector: 'stepper-overview-example',
templateUrl: 'stepper-overview-example.html',
styleUrls: ['stepper-overview-example.css'],
})
export class StepperOverviewExample implements OnInit {
isLinear = false;
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]
});
}
}
.mat-stepper-horizontal {
margin-top: 8px;
}
.mat-form-field {
margin-top: 16px;
}
Output:
app.component.html
<button mat-raised-button (click)="isLinear = !isLinear" id="toggle-linear">
{{!isLinear ? 'Enable linear mode' : 'Disable linear mode'}}
</button>
<mat-vertical-stepper [linear]="isLinear" #stepper>
<mat-step [stepControl]="firstFormGroup">
<form [formGroup]="firstFormGroup">
<ng-template matStepLabel>Fill out your name</ng-template>
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
</mat-form-field>
<div>
<button mat-button 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>
<mat-label>Address</mat-label>
<input matInput formControlName="secondCtrl" placeholder="Ex. 1 Main St, New York, NY"
required>
</mat-form-field>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step>
<ng-template matStepLabel>Done</ng-template>
<p>You are now done.</p>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button (click)="stepper.reset()">Reset</button>
</div>
</mat-step>
</mat-vertical-stepper>
app.component.ts
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
/**
* @title Stepper vertical
*/
@Component({
selector: 'stepper-vertical-example',
templateUrl: 'stepper-vertical-example.html',
styleUrls: ['stepper-vertical-example.css']
})
export class StepperVerticalExample implements OnInit {
isLinear = false;
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]
});
}
}
.mat-stepper-vertical {
margin-top: 8px;
}
.mat-form-field {
margin-top: 16px;
}
Output:
The Mat-horizontal-stepper selector can create a horizontal stepper, and the Mat-vertical-stepper used to make a vertical stepper. Mat-step components are placed inside either of the two stepper components.
Labels
The label attribute is used If a step’s label is text.
<mat-step [stepControl]="secondFormGroup" label="Fill out the address">
For more complex labels, add a template with matStepLabel directive.
<ng-template matStepLabel>Fill out your name</ng-template>
Label position
For mat-horizontal-stepper, we define the position of the label. The end is the default value, while below, it will be placed under the step icon instead of on its side. The label property controls this behavior.
<mat-horizontal-stepper labelPosition="bottom" #stepper>
Stepper buttons
There are two button instructions to support navigation between many stages: matStepperPrepret and matStepperNext.
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
Linear stepper
The linear attribute set to mat-horizontal-stepper and mat-vertical-stepper to create a linear stepper that requires the user to complete the previous steps before proceeding to the below steps. For each mat-step, the step control feature will be set to the top-level abscontrol used to check the action’s validity.
Below are two approaches. One uses a form for the stepper, but another uses the different form for each step.
Alternatively, if you do not want to use angular forms, you pass in the complete property at each step, which will not allow the user to continue until it becomes true. Note that if both absolute and phase control are set, phase control will take precedence.
<form [formGroup]="formGroup">
<mat-horizontal-stepper formArrayName="formArray" linear>
<mat-step formGroupName="0" [stepControl]="formArray.get([0])">
...
<div>
<button mat-button matStepperNext type="button">Next</button>
</div>
</mat-step>
<mat-step formGroupName="1" [stepControl]="formArray.get([1])">
...
<div>
<button mat-button matStepperPrevious type="button">Back</button>
<button mat-button matStepperNext type="button">Next</button>
</div>
</mat-step>
...
</mat-horizontal-stepper>
</form>
Using a different form for each step
<mat-vertical-stepper linear>
<mat-step [stepControl]="formGroup1">
<form [formGroup]="formGroup1">
...
</form>
</mat-step>
<mat-step [stepControl]="formGroup2">
<form [formGroup]="formGroup2">
...
</form>
</mat-step>
</mat-vertical-stepper>
Types of steps
Optional step
If completing a linear stepper step is not required, then the optional attribute is set on the mat-step.
<mat-step [stepControl]="secondFormGroup" [optional]="isOptional">
Editable step
By default, the steps are editable, which means that users can return to previously completed actions and edit their responses.
<mat-step [stepControl]="firstFormGroup" [editable]="isEditable">
Completed step
By default, the phase’s absolute attribute is corrected if the stage is valid (in the case of linear stepper) and the user has interacted with the phase. However, the user can also override the built-in attribute as required by this default complete behavior.
Overriding icons
By default, step headers use icons created and made from the Material Design icon set via the <mat-icon> elements. If you want to provide many sets of icons, you can do this by placing a MatsapperIcon for each icon that you want to override. Indicative, active, and alternative values of individual phases are available by the template variable:
<ng-template matStepperIcon="phone">
<mat-icon>call_end</mat-icon>
</ng-template>
<ng-template matStepperIcon="chat">
<mat-icon>forum</mat-icon>
</ng-template>
</mat-horizontal-stepper>
Note that you aren’t limited to using the mat-icon component when providing custom icons.
Step States
You can set a step position when you want. The position given to an icon by the default map. However, it can be overridden in the same way as above.
<mat-horizontal-stepper>
<mat-step label="Step 1" state="phone">
<p>Put down your phones.</p>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</mat-step>
<mat-step label="Step 2" state="chat">
<p>Socialize with each other.</p>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</div>
</mat-step>
<mat-step label="Step 3">
<p>You're welcome.</p>
</mat-step>
<!-- Icon overrides. -->
<ng-template matStepperIcon="phone">
<mat-icon>call_end</mat-icon>
</ng-template>
<ng-template matStepperIcon="chat">
<mat-icon>forum</mat-icon>
</ng-template>
</mat-horizontal-stepper>
In order to use the custom step states, you should add the displayDefaultIndicatorType option to the global default stepper which can be specified by providing a value for STEPPER_GLOBAL_OPTIONS in the application’s root module.
@NgModule({
providers: [
{
provide: STEPPER_GLOBAL_OPTIONS,
useValue: { displayDefaultIndicatorType: false }
}
]
})
app.component.html
<mat-horizontal-stepper #stepper>
<mat-step [stepControl]="firstFormGroup">
<form [formGroup]="firstFormGroup">
<ng-template matStepLabel>Fill out your name</ng-template>
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
</mat-form-field>
<div>
<button mat-button 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>
<mat-label>Address</mat-label>
<input matInput formControlName="secondCtrl" placeholder="Ex. 1 Main St, New York, NY"
required>
</mat-form-field>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step>
<ng-template matStepLabel>Done</ng-template>
<p>You are now done.</p>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button (click)="stepper.reset()">Reset</button>
</div>
</mat-step>
</mat-horizontal-stepper>
<mat-horizontal-stepper>
<mat-step label="Step 1" state="phone">
<p>Put down your phones.</p>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</mat-step>
<mat-step label="Step 2" state="chat">
<p>Socialize with each other.</p>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</div>
</mat-step>
<mat-step label="Step 3">
<p>You're welcome.</p>
</mat-step>
<!-- Icon overrides. -->
<ng-template matStepperIcon="phone">
<mat-icon>call_end</mat-icon>
</ng-template>
<ng-template matStepperIcon="chat">
<mat-icon>forum</mat-icon>
</ng-template>
</mat-horizontal-stepper>
app.component.ts
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {STEPPER_GLOBAL_OPTIONS} from '@angular/cdk/stepper';
/**
* @title Stepper with customized states
*/
@Component({
selector: 'stepper-states-example',
templateUrl: 'stepper-states-example.html',
styleUrls: ['stepper-states-example.css'],
providers: [{
provide: STEPPER_GLOBAL_OPTIONS, useValue: {displayDefaultIndicatorType: false}
}]
})
export class StepperStatesExample implements OnInit {
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]
});
}
}
app.component.css
.mat-stepper-horizontal {
margin-top: 8px;
}
.mat-form-field {
margin-top: 16px;
}
Output:
Error State
If you want to show an error when the user moved past a step that hasn’t been filled correctly. Then, you can set the error message by the errorMessage input and configure the stepper to show errors by the showError option in the STEPPER_GLOBAL_OPTIONS injection token.
@NgModule({
providers: [
{
provide: STEPPER_GLOBAL_OPTIONS,
useValue: { showError: true }
}
]
})
app.component.html
<mat-horizontal-stepper #stepper>
<mat-step [stepControl]="firstFormGroup" errorMessage="Name is required.">
<form [formGroup]="firstFormGroup">
<ng-template matStepLabel>Fill out your name</ng-template>
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput placeholder="Last name, First name" formControlName="firstCtrl" required>
</mat-form-field>
<div>
<p>Go to a different step to see the error state</p>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step [stepControl]="secondFormGroup" errorMessage="Address is required.">
<form [formGroup]="secondFormGroup">
<ng-template matStepLabel>Fill out your address</ng-template>
<mat-form-field>
<mat-label>Address</mat-label>
<input matInput placeholder="Ex. 1 Main St, New York, NY" formControlName="secondCtrl"
required>
</mat-form-field>
<div>
<p>Go to a different step to see the error state</p>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step>
<ng-template matStepLabel>Done</ng-template>
<p>You are now done.</p>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button (click)="stepper.reset()">Reset</button>
</div>
</mat-step>
</mat-horizontal-stepper>
app.component.ts
import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {STEPPER_GLOBAL_OPTIONS} from '@angular/cdk/stepper';
/**
* @title Stepper that displays errors in the steps
*/
@Component({
selector: 'stepper-errors-example',
templateUrl: 'stepper-errors-example.html',
styleUrls: ['stepper-errors-example.css'],
providers: [{
provide: STEPPER_GLOBAL_OPTIONS, useValue: {showError: true}
}]
})
export class StepperErrorsExample implements OnInit {
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]
});
}
}
app.component.css
.mat-form-field {
margin-top: 16px;
}
Output:
Keyboard interaction
- LEFT_ARROW: It focuses on the previous step header
- RIGHT_ARROW: It focuses on the following step header
- HOME: It focuses on the first step header
- END: It focuses on the last step header
- ENTER, SPACE: It selects the step where the focus is currently on
- TAB: Focuses It is the next tabbable element
- SHIFT+TAB: It focuses on the previous tabbable element
Localizing labels
Localization of messages is done by providing a subclass with translated values in the root module.
@NgModule({
imports: [MatStepperModule],
providers: [
{provide: MatStepperIntl, useClass: MyIntl},
],
})
export class MyApp {}
Accessibility
The stepper is treated as a tabbed view for accessibility purposes, so it is given role = “tablist” by default. The step header’s aria-selected feature and the aria-extended quality of the step content are set based on the phase selection change.
The area label or the area-labeledby gives the stepper and each step a meaningful label.
Leave a Reply