From 4f8f778c7fb430c21dabcf31e8933fe98431740c Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Thu, 3 Jan 2019 10:57:01 +0100 Subject: [PATCH 1/2] 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 --- ...-a-custom-stepper-using-the-cdk-stepper.md | 187 ++++++++++++++++++ src/cdk/stepper/stepper.ts | 33 ++-- src/lib/stepper/stepper-horizontal.html | 6 +- src/lib/stepper/stepper-vertical.html | 4 +- src/lib/stepper/stepper.spec.ts | 35 ++-- ...dk-custom-stepper-without-form-example.css | 0 ...k-custom-stepper-without-form-example.html | 8 + ...cdk-custom-stepper-without-form-example.ts | 37 ++++ .../example-custom-stepper.css | 33 ++++ .../example-custom-stepper.html | 17 ++ src/material-examples/material-module.ts | 3 + tools/public_api_guard/cdk/stepper.d.ts | 1 + 12 files changed, 332 insertions(+), 32 deletions(-) create mode 100644 guides/creating-a-custom-stepper-using-the-cdk-stepper.md create mode 100644 src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.css create mode 100644 src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.html create mode 100644 src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.ts create mode 100644 src/material-examples/cdk-custom-stepper-without-form/example-custom-stepper.css create mode 100644 src/material-examples/cdk-custom-stepper-without-form/example-custom-stepper.html diff --git a/guides/creating-a-custom-stepper-using-the-cdk-stepper.md b/guides/creating-a-custom-stepper-using-the-cdk-stepper.md new file mode 100644 index 000000000000..88ba93ca2bb0 --- /dev/null +++ b/guides/creating-a-custom-stepper-using-the-cdk-stepper.md @@ -0,0 +1,187 @@ +# Creating a custom stepper using the CDK stepper + +Using the [CDK stepper](https://material.angular.io/cdk/stepper/overview) it is possible 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: + + + +## Add CdkStepperModule to your project + +After adding the Angular CDK to your Angular project, the next step is to add the `CdkStepperModule` to your Angular module: + +```typescript +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { CdkStepperModule } from '@angular/cdk/stepper'; // this is the relevant import + +import { AppComponent } from './app.component'; + +@NgModule({ + imports: [BrowserModule, CdkStepperModule], // add the module to your imports + declarations: [AppComponent], + bootstrap: [AppComponent], +}) +export class AppModule {} +``` + +## 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__ +```typescript +import { Directionality } from '@angular/cdk/bidi'; +import { ChangeDetectorRef, Component, Input, QueryList } from '@angular/core'; +import { CdkStep, CdkStepper } from '@angular/cdk/stepper'; + +@Component({ + selector: 'app-custom-stepper', + templateUrl: './custom-stepper.component.html', + styleUrls: ['./custom-stepper.component.css'], + 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; + + constructor(dir: Directionality, changeDetectorRef: ChangeDetectorRef) { + super(dir, changeDetectorRef); + } + + 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 +
+
+

Step {{selectedIndex + 1}}/{{steps.length}}

+
+ +
+
+ + +
+
+ +
+ + + +
+
+``` + +In the `app.component.css` file we can now style the stepper however we want: + +__custom-stepper.component.css__ +```css +.container { + border: 1px solid black; + padding: 10px; + margin: 10px; +} + +.step-navigation-bar { + display: flex; + justify-content: flex-start; + margin-top: 10px; +} + +.active { + color: blue; +} + +.step { + background: transparent; + border: 0; + margin: 0 10px 0 10px; + padding: 10px; + color: black; +} + +.step.active { + color: blue; + border-bottom: 1px solid blue; +} + +.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 + + +

This is any content of "Step 1"

+
+ +

This is any content of "Step 2"

+
+
+``` + +As you can see in this example, each step needs to be wrapped inside a `` tag. + +If you want to iterate over your steps and use your own custom component you can do it, for example, this way: + +```html + + + + + +``` + +## 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.component.ts__ +```typescript +export class AppComponent { + completed = false; + + completeStep(): void { + this.completed = true; + } +} +``` + diff --git a/src/cdk/stepper/stepper.ts b/src/cdk/stepper/stepper.ts index 929a8fe83ec2..3545daf1648c 100644 --- a/src/cdk/stepper/stepper.ts +++ b/src/cdk/stepper/stepper.ts @@ -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; + /** The list of step components that the stepper is holding. */ + get steps(): QueryList { + return this._steps; + } + /** * The list of step headers of the steps in the stepper. * @deprecated Type to be changed to `QueryList`. @@ -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; diff --git a/src/lib/stepper/stepper-horizontal.html b/src/lib/stepper/stepper-horizontal.html index 991d96f05b84..0eb54b01a86e 100644 --- a/src/lib/stepper/stepper-horizontal.html +++ b/src/lib/stepper/stepper-horizontal.html @@ -1,12 +1,12 @@
- +
-
+
{ 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); diff --git a/src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.css b/src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.css new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.html b/src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.html new file mode 100644 index 000000000000..05e2b1dd3e70 --- /dev/null +++ b/src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.html @@ -0,0 +1,8 @@ + + +

This is any content of "Step 1"

+
+ +

This is any content of "Step 2"

+
+
\ No newline at end of file diff --git a/src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.ts b/src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.ts new file mode 100644 index 000000000000..099c6b289e41 --- /dev/null +++ b/src/material-examples/cdk-custom-stepper-without-form/cdk-custom-stepper-without-form-example.ts @@ -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; + + constructor(dir: Directionality, changeDetectorRef: ChangeDetectorRef) { + super(dir, changeDetectorRef); + } + + onClick(index: number): void { + this.selectedIndex = index; + } +} diff --git a/src/material-examples/cdk-custom-stepper-without-form/example-custom-stepper.css b/src/material-examples/cdk-custom-stepper-without-form/example-custom-stepper.css new file mode 100644 index 000000000000..3a453707f996 --- /dev/null +++ b/src/material-examples/cdk-custom-stepper-without-form/example-custom-stepper.css @@ -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; +} diff --git a/src/material-examples/cdk-custom-stepper-without-form/example-custom-stepper.html b/src/material-examples/cdk-custom-stepper-without-form/example-custom-stepper.html new file mode 100644 index 000000000000..411bdee41dae --- /dev/null +++ b/src/material-examples/cdk-custom-stepper-without-form/example-custom-stepper.html @@ -0,0 +1,17 @@ +
+
+

Step {{selectedIndex + 1}}/{{steps.length}}

+
+ +
+
+ +
+
+ +
+ + + +
+
\ No newline at end of file diff --git a/src/material-examples/material-module.ts b/src/material-examples/material-module.ts index 1dd9610413a9..6319b11ca1b8 100644 --- a/src/material-examples/material-module.ts +++ b/src/material-examples/material-module.ts @@ -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 { MatAutocompleteModule, MatBadgeModule, MatBottomSheetModule, MatButtonModule, MatButtonToggleModule, MatCardModule, MatCheckboxModule, MatChipsModule, MatDatepickerModule, @@ -20,6 +21,7 @@ import { A11yModule, CdkTableModule, CdkTreeModule, + CdkStepperModule, DragDropModule, MatAutocompleteModule, MatBadgeModule, @@ -62,6 +64,7 @@ import { A11yModule, CdkTableModule, CdkTreeModule, + CdkStepperModule, DragDropModule, MatAutocompleteModule, MatBadgeModule, diff --git a/tools/public_api_guard/cdk/stepper.d.ts b/tools/public_api_guard/cdk/stepper.d.ts index 311ee234cd33..7e01551690e7 100644 --- a/tools/public_api_guard/cdk/stepper.d.ts +++ b/tools/public_api_guard/cdk/stepper.d.ts @@ -41,6 +41,7 @@ export declare class CdkStepper implements AfterViewInit, OnDestroy { selected: CdkStep; selectedIndex: number; selectionChange: EventEmitter; + readonly steps: QueryList; constructor(_dir: Directionality, _changeDetectorRef: ChangeDetectorRef, _elementRef?: ElementRef | undefined, _document?: any); _getAnimationDirection(index: number): StepContentPositionState; _getFocusIndex(): number | null; From 9c9224085242bd24d0dbef58263ad11894e4a0ba Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 11 Jan 2019 19:07:41 +0100 Subject: [PATCH 2/2] Review comments --- ...-a-custom-stepper-using-the-cdk-stepper.md | 124 ++++++++---------- 1 file changed, 52 insertions(+), 72 deletions(-) diff --git a/guides/creating-a-custom-stepper-using-the-cdk-stepper.md b/guides/creating-a-custom-stepper-using-the-cdk-stepper.md index 88ba93ca2bb0..11b55934f6fb 100644 --- a/guides/creating-a-custom-stepper-using-the-cdk-stepper.md +++ b/guides/creating-a-custom-stepper-using-the-cdk-stepper.md @@ -1,45 +1,25 @@ # Creating a custom stepper using the CDK stepper -Using the [CDK stepper](https://material.angular.io/cdk/stepper/overview) it is possible to build a custom stepper which you can completely style yourself without any specific Material Design styling. +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: -## Add CdkStepperModule to your project - -After adding the Angular CDK to your Angular project, the next step is to add the `CdkStepperModule` to your Angular module: - -```typescript -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { CdkStepperModule } from '@angular/cdk/stepper'; // this is the relevant import - -import { AppComponent } from './app.component'; - -@NgModule({ - imports: [BrowserModule, CdkStepperModule], // add the module to your imports - declarations: [AppComponent], - bootstrap: [AppComponent], -}) -export class AppModule {} -``` - ## 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__ -```typescript -import { Directionality } from '@angular/cdk/bidi'; -import { ChangeDetectorRef, Component, Input, QueryList } from '@angular/core'; -import { CdkStep, CdkStepper } from '@angular/cdk/stepper'; +**custom-stepper.component.ts** +```ts @Component({ - selector: 'app-custom-stepper', - templateUrl: './custom-stepper.component.html', - styleUrls: ['./custom-stepper.component.css'], - providers: [{ provide: CdkStepper, useExisting: CustomStepperComponent }], + 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 */ @@ -51,76 +31,79 @@ export class CustomStepperComponent extends CdkStepper { /** The list of step components that the stepper is holding. */ steps: QueryList; - constructor(dir: Directionality, changeDetectorRef: ChangeDetectorRef) { - super(dir, changeDetectorRef); - } - 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). +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__ +**custom-stepper.component.html** + ```html
-
-

Step {{selectedIndex + 1}}/{{steps.length}}

-
- -
-
- - -
-
- -
- - - -
+

Step {{selectedIndex + 1}}/{{steps.length}}

+ +
+
+ + +
+
+ +
+ + + +
``` In the `app.component.css` file we can now style the stepper however we want: -__custom-stepper.component.css__ +**custom-stepper.component.css** + ```css -.container { +.example-container { border: 1px solid black; padding: 10px; margin: 10px; } -.step-navigation-bar { +.example-step-navigation-bar { display: flex; justify-content: flex-start; margin-top: 10px; } -.active { +.example-active { color: blue; } -.step { +.example-step { background: transparent; border: 0; - margin: 0 10px 0 10px; + margin: 0 10px; padding: 10px; color: black; } -.step.active { +.example-step.example-active { color: blue; border-bottom: 1px solid blue; } -.nav-button { +.example-nav-button { background: transparent; border: 0; } @@ -130,15 +113,12 @@ __custom-stepper.component.css__ 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__ +**app.component.html** + ```html - -

This is any content of "Step 1"

-
- -

This is any content of "Step 2"

-
+

This is any content of "Step 1"

+

This is any content of "Step 2"

``` @@ -148,8 +128,7 @@ If you want to iterate over your steps and use your own custom component you can ```html - + @@ -161,7 +140,8 @@ The above example allows the user to freely navigate between all steps. The `Cdk A simple example without using forms could look this way: -__app.component.html__ +**app.component.html** + ```html @@ -174,8 +154,9 @@ __app.component.html__ ``` -__app.component.ts__ -```typescript +**app.component.ts** + +```ts export class AppComponent { completed = false; @@ -184,4 +165,3 @@ export class AppComponent { } } ``` -