myHotTake

Tag: Angular step-by-step form

  • How Do You Build Multi-Step Forms in Angular?

    If you find this analogy helpful, feel free to give it a like or share—it helps me help more people!


    Imagine I’m a tailor making a custom suit. Each step of the fitting process is like a step in a multi-step form in Angular. First, I take the measurements—waist, shoulders, length—this is Step 1. I don’t want to overwhelm my client, so I just focus on one thing at a time. After the measurements are locked in, I move on to fabric selection, which is Step 2. The measurements are safely stored in my notepad, so I can come back to them whenever I need.

    Once the fabric is chosen, I proceed to Step 3: styling. The client picks lapels, buttons, and pockets. Each step builds on the previous one, but I don’t lose what I’ve already done. If my client suddenly wants to adjust their measurements, I can flip back to Step 1 in my notepad, make the changes, and move forward again—no need to start the process all over.

    In Angular, the notepad is like the shared state between components. Each step is a separate “fitting room” managed by a different child component, and the master tailor—the parent component—coordinates the flow. When my client clicks “Next” or “Back,” the parent component updates which fitting room is active, while keeping the existing details safe and ready for the final tailoring.

    Just like that, Angular’s multi-step form ensures I give my client a seamless, tailored experience without drowning them in choices all at once. The suit, like the form data, comes together step by step, polished and ready for delivery.


    Let’s take the tailor analogy and translate it into JavaScript code using Angular concepts. Each fitting room (or step) becomes a child component, the notepad becomes a shared state, and the master tailor becomes the parent component orchestrating the process.

    Parent Component (Master Tailor)

    The parent component manages the state and controls the flow between steps:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-multi-step-form',
      template: `
        <div>
          <ng-container [ngSwitch]="currentStep">
            <app-step-one 
              *ngSwitchCase="1" 
              (next)="goToStep(2)" 
              [(formData)]="formData">
            </app-step-one>
    
            <app-step-two 
              *ngSwitchCase="2" 
              (next)="goToStep(3)" 
              (back)="goToStep(1)" 
              [(formData)]="formData">
            </app-step-two>
    
            <app-step-three 
              *ngSwitchCase="3" 
              (back)="goToStep(2)" 
              [(formData)]="formData">
            </app-step-three>
          </ng-container>
        </div>
      `,
    })
    export class MultiStepFormComponent {
      currentStep = 1; // Start with Step 1
      formData = {
        measurements: {},
        fabric: '',
        styling: {}
      };
    
      goToStep(step: number) {
        this.currentStep = step;
      }
    }

    Here:

    • currentStep determines which “fitting room” (child component) is active.
    • formData acts as the notepad, storing data across steps.
    • goToStep transitions between steps.

    Child Component (Fitting Room)

    Let’s create Step One as an example:

    import { Component, Input, Output, EventEmitter } from '@angular/core';
    
    @Component({
      selector: 'app-step-one',
      template: `
        <h2>Step 1: Measurements</h2>
        <form (ngSubmit)="goNext()">
          <label>Waist:</label>
          <input [(ngModel)]="formData.measurements.waist" name="waist" />
    
          <label>Shoulders:</label>
          <input [(ngModel)]="formData.measurements.shoulders" name="shoulders" />
    
          <button type="submit">Next</button>
        </form>
      `,
    })
    export class StepOneComponent {
      @Input() formData: any = {};
      @Output() next = new EventEmitter<void>();
    
      goNext() {
        this.next.emit(); // Notify the parent to go to Step 2
      }
    }

    This component:

    • Uses @Input to receive shared state (formData) from the parent.
    • Emits an event (@Output() next) when the user is ready to move to the next step.

    Shared State Across Steps

    All steps can access and modify formData, ensuring the data persists across components. For instance:

    • Step Two: Lets users select the fabric.
    • Step Three: Lets users finalize their styling.

    Each child communicates with the parent, updating and accessing formData as needed.


    Key Takeaways

    1. Component Communication: The parent component manages the flow, while child components handle individual steps.
    2. State Management: Shared state (e.g., formData) ensures data persists across steps and can be reviewed or modified.
    3. Flexibility: The parent allows navigation between steps without resetting data, mimicking the tailor’s ability to go back and refine earlier choices.
    4. Reusability: Each step is a modular, reusable component that can be adjusted or replaced without impacting others.