mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
docs(cdk-stepper): add guide to create stepper (#14710)
* docs(cdk-stepper): add guide to create stepper * Added guide how to create a custom stepper using the CDK * Added a material example to demonstrate the custom CDK stepper * Made steps from CdkStepper public as it is necessary to build a custom stepper * Review comments
This commit is contained in:
committed by
vivian-hu
parent
3d3179f2f8
commit
f6d0ea80ca
@@ -0,0 +1,167 @@
|
||||
# Creating a custom stepper using the CDK stepper
|
||||
|
||||
The [CDK stepper](https://material.angular.io/cdk/stepper/overview) allows to build a custom stepper which you can completely style yourself without any specific Material Design styling.
|
||||
|
||||
In this guide, we'll learn how we can build our own custom stepper using the CDK stepper. Here is what we'll build by the end of this guide:
|
||||
|
||||
<!-- example(cdk-custom-stepper-without-form) -->
|
||||
|
||||
## Create our custom stepper component
|
||||
|
||||
Now we are ready to create our custom stepper component. Therefore, we need to create a new Angular component which extends `CdkStepper`:
|
||||
|
||||
**custom-stepper.component.ts**
|
||||
|
||||
```ts
|
||||
@Component({
|
||||
selector: "app-custom-stepper",
|
||||
templateUrl: "./custom-stepper.component.html",
|
||||
styleUrls: ["./custom-stepper.component.css"],
|
||||
// This custom stepper provides itself as CdkStepper so that it can be recognized
|
||||
// by other components.
|
||||
providers: [{ provide: CdkStepper, useExisting: CustomStepperComponent }]
|
||||
})
|
||||
export class CustomStepperComponent extends CdkStepper {
|
||||
/** Whether the validity of previous steps should be checked or not */
|
||||
linear: boolean;
|
||||
|
||||
/** The index of the selected step. */
|
||||
selectedIndex: number;
|
||||
|
||||
/** The list of step components that the stepper is holding. */
|
||||
steps: QueryList<CdkStep>;
|
||||
|
||||
onClick(index: number): void {
|
||||
this.selectedIndex = index;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After we've extended our component class from `CdkStepper` we can now access different properties from this class like `linear`, `selectedIndex` and `steps` which are defined in the [API documentation](https://material.angular.io/cdk/stepper/api#CdkStepper).
|
||||
|
||||
This is the HTML template of our custom stepper component:
|
||||
|
||||
**custom-stepper.component.html**
|
||||
|
||||
```html
|
||||
<section class="container">
|
||||
<header><h2>Step {{selectedIndex + 1}}/{{steps.length}}</h2></header>
|
||||
|
||||
<section *ngFor="let step of steps; let i = index;">
|
||||
<div [style.display]="selectedIndex === i ? 'block' : 'none'">
|
||||
<!-- Content from the CdkStep is projected here -->
|
||||
<ng-container [ngTemplateOutlet]="step.content"></ng-container>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="step-navigation-bar">
|
||||
<button class="nav-button" cdkStepperPrevious>←</button>
|
||||
<button
|
||||
class="step"
|
||||
*ngFor="let step of steps; let i = index;"
|
||||
[ngClass]="{'active': selectedIndex === i}"
|
||||
(click)="onClick(i)"
|
||||
>
|
||||
Step {{i + 1}}
|
||||
</button>
|
||||
<button class="nav-button" cdkStepperNext>→</button>
|
||||
</footer>
|
||||
</section>
|
||||
```
|
||||
|
||||
In the `app.component.css` file we can now style the stepper however we want:
|
||||
|
||||
**custom-stepper.component.css**
|
||||
|
||||
```css
|
||||
.example-container {
|
||||
border: 1px solid black;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.example-step-navigation-bar {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.example-active {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.example-step {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
margin: 0 10px;
|
||||
padding: 10px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.example-step.example-active {
|
||||
color: blue;
|
||||
border-bottom: 1px solid blue;
|
||||
}
|
||||
|
||||
.example-nav-button {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Using our new custom stepper component
|
||||
|
||||
Now we are ready to use our new custom stepper component and fill it with steps. Therefore we can, for example, add it to our `app.component.html` and define some steps:
|
||||
|
||||
**app.component.html**
|
||||
|
||||
```html
|
||||
<app-custom-stepper>
|
||||
<cdk-step><p>This is any content of "Step 1"</p></cdk-step>
|
||||
<cdk-step><p>This is any content of "Step 2"</p></cdk-step>
|
||||
</app-custom-stepper>
|
||||
```
|
||||
|
||||
As you can see in this example, each step needs to be wrapped inside a `<cdk-step>` tag.
|
||||
|
||||
If you want to iterate over your steps and use your own custom component you can do it, for example, this way:
|
||||
|
||||
```html
|
||||
<app-custom-stepper>
|
||||
<cdk-step *ngFor="let step of mySteps; let stepIndex = index">
|
||||
<my-step-component [step]="step"></my-step-component>
|
||||
</cdk-step>
|
||||
</app-custom-stepper>
|
||||
```
|
||||
|
||||
## Linear mode
|
||||
|
||||
The above example allows the user to freely navigate between all steps. The `CdkStepper` additionally provides the linear mode which requires the user to complete previous steps before proceeding.
|
||||
|
||||
A simple example without using forms could look this way:
|
||||
|
||||
**app.component.html**
|
||||
|
||||
```html
|
||||
<app-custom-stepper linear>
|
||||
<cdk-step editable="false" [completed]="completed">
|
||||
<input type="text" name="a" value="Cannot proceed to next step" />
|
||||
<button (click)="completeStep()">Complete step</button>
|
||||
</cdk-step>
|
||||
<cdk-step editable="false">
|
||||
<input type="text" name="b" value="b" />
|
||||
</cdk-step>
|
||||
</app-custom-stepper>
|
||||
```
|
||||
|
||||
**app.component.ts**
|
||||
|
||||
```ts
|
||||
export class AppComponent {
|
||||
completed = false;
|
||||
|
||||
completeStep(): void {
|
||||
this.completed = true;
|
||||
}
|
||||
}
|
||||
```
|
||||
+21
-12
@@ -247,9 +247,18 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
*/
|
||||
private _document: Document | undefined;
|
||||
|
||||
/** The list of step components that the stepper is holding. */
|
||||
/**
|
||||
* The list of step components that the stepper is holding.
|
||||
* @deprecated use `steps` instead
|
||||
* @breaking-change 9.0.0 remove this property
|
||||
*/
|
||||
@ContentChildren(CdkStep) _steps: QueryList<CdkStep>;
|
||||
|
||||
/** The list of step components that the stepper is holding. */
|
||||
get steps(): QueryList<CdkStep> {
|
||||
return this._steps;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of step headers of the steps in the stepper.
|
||||
* @deprecated Type to be changed to `QueryList<CdkStepHeader>`.
|
||||
@@ -267,15 +276,15 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
@Input()
|
||||
get selectedIndex() { return this._selectedIndex; }
|
||||
set selectedIndex(index: number) {
|
||||
if (this._steps) {
|
||||
if (this.steps) {
|
||||
// Ensure that the index can't be out of bounds.
|
||||
if (index < 0 || index > this._steps.length - 1) {
|
||||
if (index < 0 || index > this.steps.length - 1) {
|
||||
throw Error('cdkStepper: Cannot assign out-of-bounds value to `selectedIndex`.');
|
||||
}
|
||||
|
||||
if (this._selectedIndex != index &&
|
||||
!this._anyControlsInvalidOrPending(index) &&
|
||||
(index >= this._selectedIndex || this._steps.toArray()[index].editable)) {
|
||||
(index >= this._selectedIndex || this.steps.toArray()[index].editable)) {
|
||||
this._updateSelectedItemIndex(index);
|
||||
}
|
||||
} else {
|
||||
@@ -288,10 +297,10 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
@Input()
|
||||
get selected(): CdkStep {
|
||||
// @breaking-change 8.0.0 Change return type to `CdkStep | undefined`.
|
||||
return this._steps ? this._steps.toArray()[this.selectedIndex] : undefined!;
|
||||
return this.steps ? this.steps.toArray()[this.selectedIndex] : undefined!;
|
||||
}
|
||||
set selected(step: CdkStep) {
|
||||
this.selectedIndex = this._steps ? this._steps.toArray().indexOf(step) : -1;
|
||||
this.selectedIndex = this.steps ? this.steps.toArray().indexOf(step) : -1;
|
||||
}
|
||||
|
||||
/** Event emitted when the selected step has changed. */
|
||||
@@ -327,7 +336,7 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
|
||||
this._keyManager.updateActiveItemIndex(this._selectedIndex);
|
||||
|
||||
this._steps.changes.pipe(takeUntil(this._destroyed)).subscribe(() => {
|
||||
this.steps.changes.pipe(takeUntil(this._destroyed)).subscribe(() => {
|
||||
if (!this.selected) {
|
||||
this._selectedIndex = Math.max(this._selectedIndex - 1, 0);
|
||||
}
|
||||
@@ -341,7 +350,7 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
|
||||
/** Selects and focuses the next step in list. */
|
||||
next(): void {
|
||||
this.selectedIndex = Math.min(this._selectedIndex + 1, this._steps.length - 1);
|
||||
this.selectedIndex = Math.min(this._selectedIndex + 1, this.steps.length - 1);
|
||||
}
|
||||
|
||||
/** Selects and focuses the previous step in list. */
|
||||
@@ -352,7 +361,7 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
/** Resets the stepper to its initial state. Note that this includes clearing form data. */
|
||||
reset(): void {
|
||||
this._updateSelectedItemIndex(0);
|
||||
this._steps.forEach(step => step.reset());
|
||||
this.steps.forEach(step => step.reset());
|
||||
this._stateChanged();
|
||||
}
|
||||
|
||||
@@ -384,7 +393,7 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
|
||||
/** Returns the type of icon to be displayed. */
|
||||
_getIndicatorType(index: number, state: StepState = STEP_STATE.NUMBER): StepState {
|
||||
const step = this._steps.toArray()[index];
|
||||
const step = this.steps.toArray()[index];
|
||||
const isCurrentStep = this._isCurrentStep(index);
|
||||
|
||||
return step._displayDefaultIndicatorType
|
||||
@@ -429,7 +438,7 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
private _updateSelectedItemIndex(newIndex: number): void {
|
||||
const stepsArray = this._steps.toArray();
|
||||
const stepsArray = this.steps.toArray();
|
||||
this.selectionChange.emit({
|
||||
selectedIndex: newIndex,
|
||||
previouslySelectedIndex: this._selectedIndex,
|
||||
@@ -469,7 +478,7 @@ export class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
}
|
||||
|
||||
private _anyControlsInvalidOrPending(index: number): boolean {
|
||||
const steps = this._steps.toArray();
|
||||
const steps = this.steps.toArray();
|
||||
|
||||
steps[this._selectedIndex].interacted = true;
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<div class="mat-horizontal-stepper-header-container">
|
||||
<ng-container *ngFor="let step of _steps; let i = index; let isLast = last">
|
||||
<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-setsize]="steps.length"
|
||||
[attr.aria-controls]="_getStepContentId(i)"
|
||||
[attr.aria-selected]="selectedIndex == i"
|
||||
[attr.aria-label]="step.ariaLabel || null"
|
||||
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mat-horizontal-content-container">
|
||||
<div *ngFor="let step of _steps; let i = index"
|
||||
<div *ngFor="let step of steps; let i = index"
|
||||
class="mat-horizontal-stepper-content" role="tabpanel"
|
||||
[@stepTransition]="_getAnimationDirection(i)"
|
||||
(@stepTransition.done)="_animationDone.next($event)"
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<div class="mat-step" *ngFor="let step of _steps; let i = index; let isLast = last">
|
||||
<div class="mat-step" *ngFor="let step of steps; let i = index; let isLast = last">
|
||||
<mat-step-header class="mat-vertical-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-setsize]="steps.length"
|
||||
[attr.aria-controls]="_getStepContentId(i)"
|
||||
[attr.aria-selected]="selectedIndex === i"
|
||||
[attr.aria-label]="step.ariaLabel || null"
|
||||
|
||||
@@ -9,7 +9,12 @@ import {
|
||||
SPACE,
|
||||
UP_ARROW,
|
||||
} from '@angular/cdk/keycodes';
|
||||
import {StepperOrientation, STEPPER_GLOBAL_OPTIONS, STEP_STATE} from '@angular/cdk/stepper';
|
||||
import {
|
||||
StepperOrientation,
|
||||
STEPPER_GLOBAL_OPTIONS,
|
||||
STEP_STATE,
|
||||
CdkStep
|
||||
} from '@angular/cdk/stepper';
|
||||
import {dispatchKeyboardEvent, createKeyboardEvent, dispatchEvent} from '@angular/cdk/testing';
|
||||
import {Component, DebugElement, EventEmitter, OnInit, Type, Provider} from '@angular/core';
|
||||
import {ComponentFixture, fakeAsync, flush, inject, TestBed} from '@angular/core/testing';
|
||||
@@ -259,7 +264,7 @@ describe('MatStepper', () => {
|
||||
let stepperComponent = fixture.debugElement.query(By.directive(MatStepper)).componentInstance;
|
||||
|
||||
stepperComponent.selectedIndex = 1;
|
||||
stepperComponent._steps.toArray()[0].editable = false;
|
||||
stepperComponent.steps.toArray()[0].editable = false;
|
||||
let previousButtonNativeEl = fixture.debugElement
|
||||
.queryAll(By.directive(MatStepperPrevious))[1].nativeElement;
|
||||
previousButtonNativeEl.click();
|
||||
@@ -267,7 +272,7 @@ describe('MatStepper', () => {
|
||||
|
||||
expect(stepperComponent.selectedIndex).toBe(1);
|
||||
|
||||
stepperComponent._steps.toArray()[0].editable = true;
|
||||
stepperComponent.steps.toArray()[0].editable = true;
|
||||
previousButtonNativeEl.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -279,7 +284,7 @@ describe('MatStepper', () => {
|
||||
let nextButtonNativeEl = fixture.debugElement
|
||||
.queryAll(By.directive(MatStepperNext))[0].nativeElement;
|
||||
expect(stepperComponent._getIndicatorType(0)).toBe('number');
|
||||
stepperComponent._steps.toArray()[0].editable = true;
|
||||
stepperComponent.steps.toArray()[0].editable = true;
|
||||
nextButtonNativeEl.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -291,7 +296,7 @@ describe('MatStepper', () => {
|
||||
let nextButtonNativeEl = fixture.debugElement
|
||||
.queryAll(By.directive(MatStepperNext))[0].nativeElement;
|
||||
expect(stepperComponent._getIndicatorType(0)).toBe('number');
|
||||
stepperComponent._steps.toArray()[0].editable = false;
|
||||
stepperComponent.steps.toArray()[0].editable = false;
|
||||
nextButtonNativeEl.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -415,7 +420,7 @@ describe('MatStepper', () => {
|
||||
const stepperDebugElement = fixture.debugElement.query(By.directive(MatStepper));
|
||||
const stepperComponent: MatStepper = stepperDebugElement.componentInstance;
|
||||
|
||||
stepperComponent._steps.toArray()[0].editable = true;
|
||||
stepperComponent.steps.toArray()[0].editable = true;
|
||||
stepperComponent.next();
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -428,7 +433,7 @@ describe('MatStepper', () => {
|
||||
const stepperDebugElement = fixture.debugElement.query(By.directive(MatStepper));
|
||||
const stepperComponent: MatStepper = stepperDebugElement.componentInstance;
|
||||
|
||||
stepperComponent._steps.toArray()[0].editable = false;
|
||||
stepperComponent.steps.toArray()[0].editable = false;
|
||||
stepperComponent.next();
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -591,7 +596,7 @@ describe('MatStepper', () => {
|
||||
stepperComponent.selectedIndex = 2;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(stepperComponent._steps.toArray()[2].optional).toBe(true);
|
||||
expect(stepperComponent.steps.toArray()[2].optional).toBe(true);
|
||||
expect(stepperComponent.selectedIndex).toBe(2);
|
||||
expect(testComponent.threeGroup.get('threeCtrl')!.valid).toBe(true);
|
||||
|
||||
@@ -614,7 +619,7 @@ describe('MatStepper', () => {
|
||||
});
|
||||
|
||||
it('should be able to reset the stepper to its initial state', () => {
|
||||
const steps = stepperComponent._steps.toArray();
|
||||
const steps = stepperComponent.steps.toArray();
|
||||
|
||||
testComponent.oneGroup.get('oneCtrl')!.setValue('value');
|
||||
fixture.detectChanges();
|
||||
@@ -650,7 +655,7 @@ describe('MatStepper', () => {
|
||||
});
|
||||
|
||||
it('should reset back to the first step when some of the steps are not editable', () => {
|
||||
const steps = stepperComponent._steps.toArray();
|
||||
const steps = stepperComponent.steps.toArray();
|
||||
|
||||
steps[0].editable = false;
|
||||
|
||||
@@ -669,7 +674,7 @@ describe('MatStepper', () => {
|
||||
});
|
||||
|
||||
it('should not clobber the `complete` binding when resetting', () => {
|
||||
const steps: MatStep[] = stepperComponent._steps.toArray();
|
||||
const steps: CdkStep[] = stepperComponent.steps.toArray();
|
||||
const fillOutStepper = () => {
|
||||
testComponent.oneGroup.get('oneCtrl')!.setValue('input');
|
||||
testComponent.twoGroup.get('twoCtrl')!.setValue('input');
|
||||
@@ -933,7 +938,7 @@ describe('MatStepper', () => {
|
||||
.queryAll(By.directive(MatStepperNext))[0].nativeElement;
|
||||
|
||||
stepper.selectedIndex = 1;
|
||||
stepper._steps.first.hasError = true;
|
||||
stepper.steps.first.hasError = true;
|
||||
nextButtonNativeEl.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -950,7 +955,7 @@ describe('MatStepper', () => {
|
||||
|
||||
expect(stepper._getIndicatorType(0)).toBe(STEP_STATE.ERROR);
|
||||
|
||||
stepper._steps.first.hasError = false;
|
||||
stepper.steps.first.hasError = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(stepper._getIndicatorType(0)).not.toBe(STEP_STATE.ERROR);
|
||||
@@ -981,7 +986,7 @@ describe('MatStepper', () => {
|
||||
.queryAll(By.directive(MatStepperNext))[0].nativeElement;
|
||||
|
||||
stepper.selectedIndex = 1;
|
||||
stepper._steps.first.completed = true;
|
||||
stepper.steps.first.completed = true;
|
||||
nextButtonNativeEl.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -990,7 +995,7 @@ describe('MatStepper', () => {
|
||||
|
||||
it('should show edit state when step is editable and its the current step', () => {
|
||||
stepper.selectedIndex = 1;
|
||||
stepper._steps.toArray()[1].editable = true;
|
||||
stepper.steps.toArray()[1].editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(stepper._getIndicatorType(1)).toBe(STEP_STATE.EDIT);
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<example-custom-stepper>
|
||||
<cdk-step>
|
||||
<p>This is any content of "Step 1"</p>
|
||||
</cdk-step>
|
||||
<cdk-step>
|
||||
<p>This is any content of "Step 2"</p>
|
||||
</cdk-step>
|
||||
</example-custom-stepper>
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import {Component, QueryList, ChangeDetectorRef} from '@angular/core';
|
||||
import {CdkStepper, CdkStep} from '@angular/cdk/stepper';
|
||||
import {Directionality} from '@angular/cdk/bidi';
|
||||
|
||||
/** @title A custom CDK stepper without a form */
|
||||
@Component({
|
||||
selector: 'cdk-custom-stepper-without-form-example',
|
||||
templateUrl: './cdk-custom-stepper-without-form-example.html',
|
||||
styleUrls: ['./cdk-custom-stepper-without-form-example.css']
|
||||
})
|
||||
export class CdkCustomStepperWithoutFormExample {}
|
||||
|
||||
/** Custom CDK stepper component */
|
||||
@Component({
|
||||
selector: 'example-custom-stepper',
|
||||
templateUrl: './example-custom-stepper.html',
|
||||
styleUrls: ['./example-custom-stepper.css'],
|
||||
providers: [{ provide: CdkStepper, useExisting: CustomStepper }],
|
||||
})
|
||||
export class CustomStepper extends CdkStepper {
|
||||
/** Whether the validity of previous steps should be checked or not */
|
||||
linear: boolean;
|
||||
|
||||
/** The index of the selected step. */
|
||||
selectedIndex: number;
|
||||
|
||||
/** The list of step components that the stepper is holding. */
|
||||
steps: QueryList<CdkStep>;
|
||||
|
||||
constructor(dir: Directionality, changeDetectorRef: ChangeDetectorRef) {
|
||||
super(dir, changeDetectorRef);
|
||||
}
|
||||
|
||||
onClick(index: number): void {
|
||||
this.selectedIndex = index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
.example-container {
|
||||
border: 1px solid black;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.example-step-navigation-bar {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.example-active {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.example-step {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
margin: 0 10px;
|
||||
padding: 10px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.example-step.example-active {
|
||||
color: blue;
|
||||
border-bottom: 1px solid blue;
|
||||
}
|
||||
|
||||
.example-nav-button {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<section class="example-container">
|
||||
<header>
|
||||
<h2>Step {{selectedIndex + 1}}/{{steps.length}}</h2>
|
||||
</header>
|
||||
|
||||
<section *ngFor="let step of steps; let i = index; let isLast = last">
|
||||
<div [style.display]="selectedIndex === i ? 'block' : 'none'">
|
||||
<ng-container [ngTemplateOutlet]="step.content"></ng-container>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="example-step-navigation-bar">
|
||||
<button class="example-nav-button" cdkStepperPrevious>←</button>
|
||||
<button class="example-step" *ngFor="let step of steps; let i = index;" [ngClass]="{'example-active': selectedIndex === i}" (click)="onClick(i)">Step {{i + 1}}</button>
|
||||
<button class="example-nav-button" cdkStepperNext>→</button>
|
||||
</footer>
|
||||
</section>
|
||||
@@ -5,6 +5,7 @@ import {A11yModule} from '@angular/cdk/a11y';
|
||||
import {CdkTableModule} from '@angular/cdk/table';
|
||||
import {CdkTreeModule} from '@angular/cdk/tree';
|
||||
import {DragDropModule} from '@angular/cdk/drag-drop';
|
||||
import {CdkStepperModule} from '@angular/cdk/stepper';
|
||||
import {PortalModule} from '@angular/cdk/portal';
|
||||
import {
|
||||
MatAutocompleteModule, MatBadgeModule, MatBottomSheetModule, MatButtonModule,
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
A11yModule,
|
||||
CdkTableModule,
|
||||
CdkTreeModule,
|
||||
CdkStepperModule,
|
||||
DragDropModule,
|
||||
MatAutocompleteModule,
|
||||
MatBadgeModule,
|
||||
@@ -64,6 +66,7 @@ import {
|
||||
A11yModule,
|
||||
CdkTableModule,
|
||||
CdkTreeModule,
|
||||
CdkStepperModule,
|
||||
DragDropModule,
|
||||
MatAutocompleteModule,
|
||||
MatBadgeModule,
|
||||
|
||||
+1
@@ -41,6 +41,7 @@ export declare class CdkStepper implements AfterViewInit, OnDestroy {
|
||||
selected: CdkStep;
|
||||
selectedIndex: number;
|
||||
selectionChange: EventEmitter<StepperSelectionEvent>;
|
||||
readonly steps: QueryList<CdkStep>;
|
||||
constructor(_dir: Directionality, _changeDetectorRef: ChangeDetectorRef, _elementRef?: ElementRef<HTMLElement> | undefined, _document?: any);
|
||||
_getAnimationDirection(index: number): StepContentPositionState;
|
||||
_getFocusIndex(): number | null;
|
||||
|
||||
Reference in New Issue
Block a user