Here's an approach I used for this, by projecting the content of each step into another component. This avoids using a service, event handlers, etc.
In my case I wanted to project the current step into an Expansion Panel alongside the stepper. This example illustrates how you can populate the various sections of this from the stepper, using buttons in the panel's action row to navigate the stepper:
<mat-stepper #stepper orientation="vertical">
<ng-template #expansionPanelHeader>
<ng-container [ngTemplateOutlet]="stepper.selected?.stepLabel.template"></ng-container>
</ng-template>
<ng-template #expansionPanelContent>
<ng-container [ngTemplateOutlet]="stepper.selected?.content"></ng-container>
</ng-template>
<ng-template #expansionPanelActionRow>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</ng-template>
<mat-step>
<ng-template matStepLabel>Step 1</ng-template>
<ng-template matStepContent>
This is the content of step 1.
</ng-template>
</mat-step>
<mat-step>
<ng-template matStepLabel>Step 2</ng-template>
<ng-template matStepContent>
This is the content of step 2.
</ng-template>
</mat-step>
</mat-stepper>
<!-- The current step will be projected into this component -->
<mat-expansion-panel>
<mat-expansion-panel-header>
<ng-container [ngTemplateOutlet]="expansionPanelHeader"></ng-container>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<ng-container [ngTemplateOutlet]="expansionPanelContent"></ng-container>
</ng-template>
<mat-action-row>
<ng-container [ngTemplateOutlet]="expansionPanelActionRow"></ng-container>
</mat-action-row>
</mat-expansion-panel>
Unfortunately the only way I could find to prevent the stepper content from also appearing inside the stepper was this bit of CSS:
.mat-vertical-content-container {
display: none !important;
}
The CSS needs to be placed either in a global .scss
file or you will need to use ng-deep
or some other such "solution". I prefer the former, scoped to the containing elements.