Reputation: 1137
Im using the horizontal mat-stepper as shown in this stackblitz: stackblitz here
I want to position the header (where the steps are) below the content rather than above it. I see its easily done when just swapping the elements one below the other in the Elements tab inside Chrome devtools, but those elements arent exposed. how would I do that? thanks.
Upvotes: 0
Views: 4056
Reputation: 895
There is no official way of doing this.
But you can "hack" into the stepper component very easily using attribute directive.
First execute ng generate directive stepper-position
to generate new directive.
Then go to stepper-postion.directive.ts
and paste this code:
import { AfterViewInit, Directive, ElementRef, Input } from '@angular/core';
@Directive({
selector: '[appStepperPosition]'
})
export class StepperPositionDirective implements AfterViewInit {
@Input('appStepperPosition') position: 'top' | 'bottom';
element: any;
constructor(private elementRef: ElementRef) {
this.element = elementRef.nativeElement;
}
ngAfterViewInit(): void {
if (this.position === 'bottom') {
const header = this.element.children[0];
const content = this.element.children[1];
this.element.insertBefore(content, header);
}
}
}
At last go to a html template where you've declared your mat-horizontal-stepper
and add appStepperPosition="bottom"
attribute.
For example:
<mat-horizontal-stepper appStepperPosition="bottom" [linear]="isLinear" #stepper>
And now you have stepper content above the header 😀
Upvotes: 2
Reputation: 7239
You'll need to create a custom stepper component. I had to do this because I wanted the vertical stepper to show a summary of the steps that were completed. Luckily, all the component code is on GitHub.
Copy over a couple the files from https://github.com/angular/components/tree/master/src/material/stepper into a component folder.
Create a new custom stepper component that uses the new layout. I'll call it CustomHorizontalStepper
.
custom-horizontal-stepper.ts
import { MatStepper, matStepperAnimations } from "@angular/material";
@Component({
selector: 'custom-horizontal-stepper',
exportAs: 'customHorizontalStepper',
templateUrl: 'stepper-horizontal.html',
styleUrls: ['stepper.css'],
inputs: ['selectedIndex'],
host: {
'class': 'mat-stepper-horizontal',
'[class.mat-stepper-label-position-end]': 'labelPosition == "end"',
'[class.mat-stepper-label-position-bottom]': 'labelPosition == "bottom"',
'aria-orientation': 'horizontal',
'role': 'tablist',
},
animations: [matStepperAnimations.horizontalStepTransition],
providers: [
{provide: MatStepper, useExisting: CustomHorizontalStepper},
{provide: CdkStepper, useExisting: CustomHorizontalStepper}
],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomHorizontalStepper extends MatStepper {
/** Whether the label should display in bottom or end position. */
@Input()
labelPosition: 'bottom' | 'end' = 'end';
static ngAcceptInputType_editable: BooleanInput;
static ngAcceptInputType_optional: BooleanInput;
static ngAcceptInputType_completed: BooleanInput;
static ngAcceptInputType_hasError: BooleanInput;
}
Alter the stepper-horizontal.html
file.
stepper-horizontal.html
<div class="mat-horizontal-content-container">
<div *ngFor="let step of steps; let i = index"
class="mat-horizontal-stepper-content" role="tabpanel"
[@stepTransition]="_getAnimationDirection(i)"
(@stepTransition.done)="_animationDone.next($event)"
[id]="_getStepContentId(i)"
[attr.aria-labelledby]="_getStepLabelId(i)"
[attr.aria-expanded]="selectedIndex === i">
<ng-container [ngTemplateOutlet]="step.content"></ng-container>
</div>
</div>
<div class="mat-horizontal-stepper-header-container">
<ng-container *ngFor="let step of steps; let i = index; let isLast = last">
<mat-step-header class="mat-horizontal-stepper-header"
(click)="step.select()"
(keydown)="_onKeydown($event)"
[tabIndex]="_getFocusIndex() === i ? 0 : -1"
[id]="_getStepLabelId(i)"
[attr.aria-posinset]="i + 1"
[attr.aria-setsize]="steps.length"
[attr.aria-controls]="_getStepContentId(i)"
[attr.aria-selected]="selectedIndex == i"
[attr.aria-label]="step.ariaLabel || null"
[attr.aria-labelledby]="(!step.ariaLabel && step.ariaLabelledby) ? step.ariaLabelledby : null"
[index]="i"
[state]="_getIndicatorType(i, step.state)"
[label]="step.stepLabel || step.label"
[selected]="selectedIndex === i"
[active]="step.completed || selectedIndex === i || !linear"
[optional]="step.optional"
[errorMessage]="step.errorMessage"
[iconOverrides]="_iconOverrides"
[disableRipple]="disableRipple">
</mat-step-header>
<div *ngIf="!isLast" class="mat-stepper-horizontal-line"></div>
</ng-container>
</div>
Register your component in the module and use it as you would the regular stepper. Just use the new selector.
<custom-horizontal-stepper></custom-horizontal-stepper>
Upvotes: 1