The TimelineWizard component is designed to facilitate the creation of a timeline-based wizard in Angular applications. This component allows you to create a series of steps, each represented by a component, and navigate through them using a timeline. The TimelineWizard component provides features such as step navigation, asynchronous validation, and dynamic component rendering. Each step component must extend TimelineBaseComponent and can optionally include forms, custom data mapping, and asynchronous operations.
Examples
Example 1: Basic Wizard Setup
In the following example, the wizard comprises four distinct steps. The timelineSteps array defines the sequential order of each step. Three of these steps, namely "Configure Operator," "Configure Service," and "Configure Runtime Properties," involve form input. The last step, labeled "Review," acts as a comprehensive summary, consolidating data from the preceding steps for presentation.
import {ClrTimelineStepState} from'@clr/angular';
import {TimelineStep, TimelineWizardComponent} from'clr-lift';
import {Deployment} from'./deployment.type';
@Component({
imports: [TimelineWizardComponent],
template: `
<cll-timeline-wizard
[timelineSteps]="timelineSteps"
(canceled)="onCanceled()"
(confirmed)="onConfirmed($event)"
(finished)="onFinished()"
></cll-timeline-wizard>
`
})
exportclassTimelineWizardDemoComponent {
// simulate an API response. These values will be set into step forms.initialData: Deployment = {
operator: {
name: 'my-operator',
namespace: 'operator-namespace',
},
service: {
cpu: 2,
replicas: 4,
url: 'https://example.service.com',
},
appProperties: {
'java.runtime.debug': 'true',
},
};
readonlytimelineSteps: TimelineStep[] = [
{
state: ClrTimelineStepState.CURRENT,
title: 'Configure Operator',
id: 'operator', // use id to find the step datacomponent: ConfigureOperatorComponent,
data: {operator: this.initialData.operator},
},
{
state: ClrTimelineStepState.NOT_STARTED,
title: 'Configure Service',
id: 'service',
component: ConfigureServiceComponent,
data: {service: this.initialData.service},
},
{
state: ClrTimelineStepState.NOT_STARTED,
title: 'Configure Runtime Properties', // use title to find the step, id is optionalcomponent: ConfigureRuntimePropComponent,
data: {appProperties: this.initialData.appProperties},
},
{
state: ClrTimelineStepState.NOT_STARTED,
title: 'Review',
component: ConfigureReviewComponent,
},
];
onCanceled() {
window.alert('canceled');
}
onConfirmed(data: unknown) {
window.alert('confirmed to submit, you can see form data from console. Simulate API request');
console.log(data);
}
onFinished() {
window.alert('finished');
}
}
Every step component is required to extend the base class, TimelineBaseComponent, specifying the pertinent step type. The significance of this specification lies in the fact that the currentStepData type aligns with the type defined in TimelineBaseComponent. For form-based steps, it is essential to override the form property and instantiate the FormGroup. There exist two methods to retrieve the current step data and all steps data. Firstly, you can employ the TimelineWizardService by invoking the getStepData method with the step id or title as the identifier. Alternatively, within the ngOnInit lifecycle hook, you can access currentStepData and allStepsData —these two properties are input properties in TimelineBaseComponent. It is imperative to note that attempting to access these properties in the constructor is prohibited due to their unavailability at that particular stage of the component lifecycle.
import {TimelineBaseComponent, TimelineWizardService} from'clr-lift';
import {Component, inject, OnInit} from'@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from'@angular/forms';
import {Deployment} from'../deployment.type';
@Component({
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form">
<p class="clr-required-mark">Required Information</p>
<clr-input-container>
<label class="clr-required-mark">Name</label>
<input type="text" clrInput [formControl]="form.controls.operator.controls.name" />
<clr-control-error> Required </clr-control-error>
</clr-input-container>
<clr-input-container>
<label class="clr-required-mark">Namespace</label>
<input type="text" clrInput [formControl]="form.controls.operator.controls.namespace" />
<clr-control-error> Required </clr-control-error>
</clr-input-container>
</form>
`
})
exportclassConfigureOperatorComponentextendsTimelineBaseComponent<Deployment['operator']> implementsOnInit {
private timelineWizardService = inject(TimelineWizardService);
override form = newFormGroup({
operator: newFormGroup({
name: newFormControl('', [Validators.required]),
namespace: newFormControl('', [Validators.required]),
}),
});
// use id 'operator' to find the step
stepData = this.timelineWizardService.getStepData<Pick<Deployment, 'operator'>>('operator');
constructor() {
super();
// currentStepData shape comes from TimelineBaseComponent<Deployment['operator']>.// Note: currentStepData is not available in constructor, use ngOnInit instead
}
ngOnInit() {
// currentStepData will receive the @Input data hereconsole.log(this.currentStepData);
console.log(this.stepData); // also available from service
}
}
Example 3: Custom Data Mapping
In situations where your FormGroup model does not precisely align with the data structure outlined in the timelineSteps, customization of the mapping logic becomes necessary. This involves the overriding of two key methods: formValueToData and dataToFormValue. Consider a scenario where the FormGroup consists of three controls—cpu, replicas, and url. Contrastingly, the timelineSteps data structure follows the pattern service: cpu, replicas, url. This discrepancy necessitates adjustments to ensure compatibility.
Example 4: Advanced Data Mapping with Key-Value Inputs
Illustrating a more advanced use case, consider the following example that delves into the intricacies of dataToFormValue and formValueToData customization using the KeyValueInputsComponent:
To understand more about KeyValueInputsComponent, please visit Key Value Inputs.
Example 5: Review Step with API Submission
In certain scenarios, your application might require a review step to consolidate and confirm the submission of all preceding form values. Unlike previous step components, there's no need to override the form in the review step, as it doesn't involve any form controls. The key focus is on aggregating data from the preceding steps. Utilize the TimelineWizardService method getStepData to fetch the necessary data and present it in the UI accordingly. Once you've verified that all the data is accurate, you can proceed to submit it. To accomplish this, you'll need to perform two main tasks. First, you must aggregate all the steps' data and map it to the desired API payload. The second task involves triggering the API call. You can leverage the timelineWizardService.allStepsData to obtain all data. However, note that the shape is an array containing each step's id or title as an identifier along with the respective step data. To address this, you can create a custom data conversion function, such as the buildPayload method outlined below. Subsequently, to initiate the API request, override the next$ observable to construct your API stream.
You can customize the next$ observable in any step, enabling the inclusion of both asynchronous and synchronous operations, such as an API call.
Example 6: Interactive Demo
Try the interactive wizard below:
Configure Operator
Configure Operator
Configure Service
Configure Service
Configure Runtime Properties
Configure Runtime Properties
Review
Review
API Reference
TimelineWizardComponent
The main component responsible for managing the timeline wizard.
Inputs
timelineSteps: TimelineStep[]
(Required) An array of TimelineStep objects representing the steps in the wizard.
live?: boolean
Controls whether to destroy step components when clicking prev/next buttons. Defaults to false.
confirmButtonText?: string
The text for the "Finish" button on the last step. Defaults to "Finish" (translated).
Outputs
confirmed: EventEmitter<unknown>
Emits all previous steps' data when clicking the finish button in the last step.
canceled: EventEmitter<void>
Emits when the wizard is canceled.
finished: EventEmitter<void>
Emits when the wizard is successfully completed.
Methods
cancel(): void
Cancels the wizard and emits the canceled event.
nextStep(): void
Moves to the next step in the wizard.
previousStep(): void
Moves to the previous step in the wizard.
TimelineBaseComponent
The base class for all step components within the timeline wizard. It provides essential properties and methods for managing the wizard steps. Extend this abstract class for each step component and override specific properties and functions accordingly.
Properties
allStepsData: unknown[]
An array containing the data of all wizard steps. Available as an input property, accessible in ngOnInit and later lifecycle hooks.
currentStepData: T | null
The data of the current step or null if not available. Available as an input property, accessible in ngOnInit and later lifecycle hooks.
form: FormGroup | null
The form group associated with the step. Set to null for steps without a form. Override this property in your step component.
next$: Observable<unknown>
An observable that emits when the "Next" button is clicked. Override it to perform custom actions, such as API calls.
stepInvalid: boolean
Indicates whether the step's form is invalid.
Methods
formValueToData(): unknown
Converts the step's form value to data. Override this method if your form structure doesn't match the step data structure.
dataToFormValue(data: any): any
Converts step data to the form value that can be used for patchValue. Override this method if your form structure doesn't match the step data structure.
TimelineStep Interface
The interface defines the properties of a step in the timeline. You can pass an array of steps to the TimelineWizard component as inputs. The shape of this input should match this interface.
Properties
state: ClrTimelineStepState
The state of the timeline step (e.g., 'not-started', 'current', 'success', 'error').
header?: string
The header of the timeline step.
title: string
(Required) The title of the timeline step, acting as an identifier (should be unique). Can be used to find step data via getStepData.
id?: string
The id of the timeline step, acting as a primary identifier. If id is not provided, will use title as identifier instead. Can be used to find step data via getStepData.
description?: string
The description of the timeline step.
component: Type<any>
(Required) The Angular component associated with the timeline step. Must extend TimelineBaseComponent.
data?: unknown
Step data, which will be converted to form data by the timeline wizard component and passed to the step component as currentStepData.
TimelineWizardService
A service that manages the state and data of the timeline wizard. It provides methods to retrieve steps' data and the current step.