This is a solution to the Multi-step form challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.
Users should be able to:
- Complete each step of the sequence
- See a summary of their selections on the final step and confirm their order
- View the optimal layout for the interface depending on their device's screen size
- See hover and focus states for all interactive elements on the page
Solution URL: here | Live Site URL: here
Mobile Screenshots @ 375px
Desktop Screenshots @ 1440px
Initially found it challenging to figure out how to decide how to arrange the form and where state should live. I started with everything inside one form component, but as the component grew it became difficult to keep track of everything, So I ultimately decided to have a <Form>
(code | live) component with all of the nested childeren for each step. Each step has a decent amount of functionaly and styling so I decided this was the best approach.
- I knew that I would like to have seperate components for the
<step-tracker-icons>
(code | live), and the<progression-buttons>
(code | live) and the<form>
(code | live). Initally I used@Input()
's and@Output()
's within the form to update theactiveStep
andstepForm
. - I knew that I would later move
activeStep
andstepForm
to a service, for better readability, maintainabilty, and scalability. - In
FormService
theactiveStep
piece of state can be viewed and updated with an Observable.
...
private activeStepSubject = new BehaviorSubject<number>(1);
activeStep$ = this.activeStepSubject.as<b>Observable</b>();
...
goToNextStep(number: number) {
this.activeStepSubject.next(number + 1);
}
- Components can then access
activeStep$
by subscribing to the Observable
this.formService.activeStep$.subscribe(
step => this.activeStep$ = step
);
- The reactive
stepForm
also lives within theFormService
so that it can be viewed and updated by multiple components as necessary
multiStepForm: FormGroup = this.fb.group({
personalDetails: this.fb.group({
name: ['', [Validators.required, Validators.minLength(4)]],
email: ['', [Validators.required, Validators.email]],
phone: ['', [Validators.required, Validators.minLength(10)]],
}),
planDetails: this.fb.group({
plan: ['arcade', [Validators.required]],
duration: ['monthly', [Validators.required]],
planCost: [9],
totalCost: [9]
}),
addOnDetails: this.fb.group({
service: [false],
serviceCost: [0],
storage: [false],
storageCost: [0],
customization: [false],
customizationCost: [0],
})
})
-
FormGroupDirective
-
How to work with Material UI components
-
How to use a Service to manage data that can be shared between multiple components
- Things get a bit wonky around the tablet breakpoint of 768px, so I would like to add a few tablet styles.
- There's not much I would add to this, but this multi-step form would serve as a great tempalte for future projects.
-
Split an Angular Reactive Form model into child components - This helped me for XYZ reason. I really liked this pattern and will use it going forward.
-
Material Button toggle - This helped me for XYZ reason. I really liked this pattern and will use it going forward.
<mat-button-toggle>
are on/off toggles with the appearance of a button. -
Material Checkbox - This is an amazing article which helped me finally understand XYZ. I'd recommend it to anyone still learning this concept.
<mat-checkbox>
provides the same functionality as a native<input type="checkbox">
enhanced with Material Design styling and animations. -
Creating a Custom @NgModule for Material Components in Angular - To keep AppModule clean, we can create another NgModule that takes care of importing Material modules to use.
- Frontend Mentor - @Chanda-Abdul
- Website - Chanda Codes
- GitHub - github.com/Chanda-Abdul