How To Create a Multi-Step Form in Angular

Create a form in Angular with multiple steps and validation for each step.


Multi-Step form

Creating a multi-step form in Angular involves setting a few things up. We will be using the ReactiveForms module.

Setting up the data

Firstly, let set up the data. I have created an object for each step in the form.

const USER_STEP = {
    email: ["", [Validators.required, Validators.email]]
    // ...
};

const PERSONAL_STEP = {
    name: ["", [Validators.required]]
    // ...
};

We will use the separation later, but now, lets import it to the formBuilder.

this.multiForm = this.formBuilder.group({
    ...this.USER_STEP,
    ...this.PERSONAL_STEP
});

I also like to create a getter for that particular form, making it easier and cleaner to get the values.

get f() {
    return this.multiForm.controls;
}

We also need some way to keep track of the current step, so let’s just add a property for that.

currentStep = 1;

Setting up the HTML

I will not dive deep into the form structure, only the overall structure that allows for the multi step process.

<div>
    <form [formGroup]="multiForm">
        <div *ngIf="currentStep === 1">
            <!-- FIRST STEP CONTENT -->
            <button (click)="next($event)">
                Next
            </button>
        </div>

        <div *ngIf="currentStep === 2">
            <!-- SECOND STEP CONTENT -->
            <button (click)="next($event)">
                Next
            </button>
        </div>
    </form>
</div>

Validation

We need to add validation to each step. It should not be possible to progress without all fields being valid.

First of all, we need the keys of each input data. Like email, and name, to see if they are valid.

We need to save them down to a property in the constructor.

this.userKeys = Object.keys(this.USER_STEP).filter(key => key !== undefined);

this.personalKeys = Object.keys(this.PERSONAL_STEP).filter(
    key => key !== undefined
);

This will filter out all the keys if they are not undefined.

We also need to create a validator function for each step, that checks if all forms within that specific step is valid.

allUserFieldsValid(): boolean {
    return this.userKeys.every(key => this.f[key].valid === true);
}

allPersonalFieldsValid(): boolean {
    return this.personalKeys.every(key => this.f[key].valid === true);
}

We are using the f getter to look that specific key up, and if it all keys in that step are valid, this function will return true.

Next button

The final logic goes into the next button.

next(e: Event) {
    e.preventDefault();

    // check if first step is valid
    if (this.currentStep === 1 && this.allUserFieldsValid()) {
        this.currentStep += 1;
    }

    // check if second step is valid
    if (this.currentStep === 2 && this.allPersonalFieldsValid()) {
        this.currentStep += 1;
    }
}