Building A Multi Step Form Wizard In Angular – Part 1

This article is part of a series on creating multi step form wizards in Angular.

Part 1 – Basic Setup
Part 2 – Building Our Forms
Part 3 – Tying It All Together


I recently came across a question on social media asking what the best way to create a “multi stage” form wizard in Angular would be. Now multi stage might be one word for it, but I’ve also seen it called multi step, multi page, multi form, hell even just “form wizard”. But they all roughly mean the same thing – a big long form that should be done in one sitting, but is broken up (often into bite size sections) across multiple “pages”.

The solutions on social media mostly involved complex third party libraries, like piping results around using rxJS, or creative use of reactive forms in Angular etc. I might be showing my age here… But I’ve built multi step forms in the exact same way since Angular 1.6. Using a shared service to hold the form “state”, and simply sharing that service between different forms/components. I’m going to outline that solution here in a very simple way so that (hopefully), you can follow on and realize that while Angular can be powerful, it can often handle complex scenarios with very non-complex code.

Project Setup

The first thing we need to do is create a brand new project for the walkthrough. Using Angular CLI we can run our standard angular new project command :

ng new multistageform

Make sure to select “Yes” when asked if I wanted to add Angular Routing as we need this to route between different steps of the form.

Inside our app.component.html we want to replace the default content in here with :

<h1>My Multistep Form</h1>
<router-outlet></router-outlet>

This will be the starting point for our form. In a real world scenario we would probably go with some sort of nested routing strategy, but for our first cut, let’s make things simple! Fire up our angular serve command in the CLI :

ng serve

And we should be able to browse to localhost:4200 and be greeted with our project all nicely set up!

Shared Service Setup

The secret to making this multi step form work is a shared service that can hold “state” as we click next throughout the form. Go ahead and run the following CLI command¬† to generate a new service called “FormData”. I personally like throwing all my services into a services folder – it just makes things a bit easier to find.

ng generate service services/FormData

This should create a new folder called services in your app directory, and inside it should be form-data.service.ts. Also note that it’s decorated with the following attribute :

@Injectable({
  providedIn: 'root'
})

This just happens by default but it’s actually incredibly important to what we are doing. The providedIn : ‘root’ property tells Angular that the service should be created at the root level, and re-used for every component/service that requests it. In simple terms, our service will be a singleton. It will be created once, and that instance will be reused everywhere. This is super important for us because as we step through the form, we want to “share” data between steps and right at the end be able to grab all the data at once.

Let’s create a model to hold some data.

ng generate class models/PersonalDetails

This will generate a class to hold the “personal details” step of the form – just a first/last name for now. Replace the contents of your newly created class with the following :

export class PersonalDetails {
    firstName : string;
    lastName : string;
}

Go ahead and do the same and create an AddressDetails model

ng generate class models/AddressDetails

And fill it with the following class definition

export class AddressDetails {
    fullAddress : string;
}

Let’s head back to our FormDataService as we need to add references to our two new models.

There’s a few different ways we could go about this, we could do it with methods, properties, or just plain public variables. For now let’s just go with public variables. We will also want to “reset” our variables in the constructor which essentially means that on page load, we have an “empty” form.

So with all that in mind, our FormDataService should end up looking like :

@Injectable({
  providedIn: 'root'
})
export class FormDataService{

  personalDetails : PersonalDetails;
  addressDetails : AddressDetails;

  constructor() {
    this.personalDetails = new PersonalDetails();
    this.addressDetails = new AddressDetails();
   }
}

That’s all we will be doing with the service for now as we jump into actually building out our forms!

Following along so far? I’ve broken up the tutorial into bite sized chunks so it’s a little less daunting. Check out the next part of this series where we build out our multi step form with some very simple HTML and Angular components.

Leave a Reply

Your email address will not be published. Required fields are marked *