My main goal is to create a component that can wrap around MatStepper and accept 2..n steps, each with their own components.
In other languages, I would typically create an interface with common behavior, implement it in different components, and then use the interface within the wrapper component.
wizard-step.component.ts
export interface WizardStep {
isValid: boolean;
nextClicked(e);
previousClicked(e);
}
wizard.component.ts
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { ProgressBarType } from '../progress-bar/ProgressBarType';
import { MatStepper } from '@angular/material';
import { WizardStep } from './wizard-step.component';
@Component({
selector: 'app-wizard',
templateUrl: './wizard.component.html',
styleUrls: [ './wizard.component.css' ]
})
export class WizardComponent implements OnInit {
progress: number;
progressBarType = ProgressBarType.Progress;
@Input() steps: WizardStep[] = [];
/**
* The material stepper instance.
*/
@ViewChild('stepper') private stepper: MatStepper;
constructor() {
}
ngOnInit() {
this.calculateProgress(0);
}
public calculateProgress(index: number): void {
this.progress = ((index + 1) / this.steps.length) * 100;
}
public next(e): void {
this.steps[e.selectedIndex].nextClicked(e);
}
public previous(e): void {
this.steps[e.selectedIndex].previousClicked(e);
}
public selectionChange(e):void {
this.calculateProgress(e.selectedIndex);
}
}
wizard.component.html
<panel>
<progress-bar class="progress-margins" [progress]="progress" [type]="progressBarType"></progress-bar>
<mat-horizontal-stepper class="hide-header" #stepper (selectionChange)="selectionChange($event)">
<mat-step *ngFor="let step of steps">
<ng-container *ngComponentOutlet="step"></ng-container>
</mat-step>
</mat-horizontal-stepper>
<form-buttons
primaryLabel="Next"
(primaryButtonClicked)="stepper.next(); next($event)"
secondaryLabel="Previous"
(secondaryButtonClicked)="stepper.previous(); previous($event)">
</form-buttons>
</panel>
To use this component, I am creating a validation component with individual components for each wizard step:
wizard-validation.component.ts
import { Component, OnInit } from '@angular/core';
import { WizardStep } from 'framework';
import { WizardValidationStep1Component } from '../wizard-validation-step1/wizard-validation-step1.component';
import { WizardValidationStep2Component } from '../wizard-validation-step2/wizard-validation-step2.component';
import { WizardValidationStep3Component } from '../wizard-validation-step3/wizard-validation-step3.component';
@Component({
selector: 'app-wizard-validation',
templateUrl: './wizard-validation.component.html',
styleUrls: []
})
export class WizardValidationComponent implements OnInit {
steps: WizardStep[] = [];
constructor() {
}
ngOnInit() {
this.steps.push(WizardValidationStep1Component);
this.steps.push(WizardValidationStep2Component);
this.steps.push(WizardValidationStep3Component);
}
}
wizard-validation.component.html
<wizard [steps]="steps">
</wizard>
And the WizardValidationStep1Component, WizardValidationStep2Component, and WizardValidationStep3Component components are currently identical:
import { Component } from '@angular/core';
import { WizardStep } from 'framework';
@Component({
selector: 'app-wizard-validation-step1',
templateUrl: './wizard-validation-step1.component.html',
styleUrls: []
})
export class WizardValidationStep1Component implements WizardStep {
isValid: boolean;
stepName = 'Step 1';
constructor() {
this.isValid = true;
}
nextClicked(e) {
alert('Clicked next on ' + this.stepName);
}
previousClicked(e) {
alert('Clicked previous on ' + this.stepName);
}
}
The wizard-validation-step1.component.html file contains:
<p>
wizard-validation-step1 works!
</p>
In wizard.component.ts, using an untyped array instead of a WizardStep array allows the component to work smoothly, but the step components don't register the clicks.
@Input() steps = [];
When attempting to run ng build
, the following errors occur:
ERROR in src/app/wizard-validation/wizard-validation.component.ts(20,21): error TS2345: Argument of type 'typeof WizardValidationStep1Component' is not assignable to parameter of type 'WizardStep'.
Property 'isValid' is missing in type 'typeof WizardValidationStep1Component'.
src/app/wizard-validation/wizard-validation.component.ts(21,21): error TS2345: Argument of type 'typeof WizardValidationStep2Component' is not assignable to parameter of type 'WizardStep'.
Property 'isValid' is missing in type 'typeof WizardValidationStep2Component'.
src/app/wizard-validation/wizard-validation.component.ts(22,21): error TS2345: Argument of type 'typeof WizardValidationStep3Component' is not assignable to parameter of type 'WizardStep'.
Property 'isValid' is missing in type 'typeof WizardValidationStep3Component'.
Is the approach I am taking valid in Angular 5/6? If not, what is another way to achieve my objective?