Angular: Multi-Step Form — with Reactive Forms Sorcery

Harshdeep Bilaiya
7 min readAug 27, 2019

--

A form is one of the most widely used web components. Won’t you agree? Be it blog, travel, restaurant, bank, hospital — pick any web application, and you will require to fill a form whenever you need to submit any information. Heck! You might not even be able to use certain applications, unless you create an account or log in to them.

Now, as a web developer I am always stumbling upon form creation tasks every now and then. On one hand, I want to be able to finish this task on hand quickly, and on the other I want seamless experience for my end-users. But with ever changing client requirements, I also want to be able to scale a simple 2-field form into something large, say, a recruitment form — with various sub-forms for collecting personal, professional, educational details of a candidate. Neither do I want to keep adding to, updating or removing from my HTML template every time there is a requirement change. How do I sit about and decide on the best approach to develop such a form?

What’s your style? [uicookies]

Reactive Forms to the Rescue

I am sure, that if you have worked with Angular in the past, you must be aware of this awesome feature provided by Angular, out of the box — Reactive forms. They are highly flexible, customisable, unit testable…and all that — true. But I want to show you how we can tap into the real power of FormGroups and FormControls and exploit them to our advantage.

I am presenting a small, yet efficient way (at least in my head) to develop your Angular form component in such a way, that it could be used as a 2-field login form, as well as, a multi-step information gathering one. Let’s get started, shall we?

First things, First — What do we need?

Before moving to the good, hands-on parts, I would like to publish a brief disclaimer: I would be neglecting a few development standards and best practices here; plus, the final application would be plain and bland; not very modular. I request you to fill in the gaps wherever you find fit. On to the good parts…

We need the magical ReactiveFormsModule, along with FormsModule in our app.module.ts (or your feature module).

app.module.ts — Initial Stage

Now, let’s think how we shall proceed with developing the actual form component. I am keeping it simple and calling it MultiStepFormComponent. Since I want this form to be scalable, I will create it by feeding it a configuration object. It goes like this —

1. Create a configuration object, which holds input field parameters, such as: type, formControlName, placeholder, etc.

2. After defining this object, we shall move on to create FormGroups, which will have FormControls and Validators, as per object definition.

3. Once, we are done with writing code which would actually do all the above, we shall move on to create the HTML template for our form. It shall have buttons to move to previous or next forms, as well as, error messages corresponding to defined form fields. We can keep submit button as a part of preview & submit step, where we shall finally make the POST request with data combined from all the forms.

One thing we must keep in mind is that, all our configuration lies in this object, let’s call it STEP_ITEMS — everything, from input type, to validations that inputs should have, to error messages you want to display on invalid input. EVERYTHING! The objective being, to keep configuration separate and easy to modify or update.

Step 1: Create the Configuration Object

multi-step-form.ts — Config object

I expect the final forms to look something like this:

Form 1
Form 2
Form 3
Review & Submit

The keys of each of object will hold formControlName, which will connect our form fields with respective formGroups and we shall see sorcery happen.

Moving on…let’s create the MultiStepFormComponent now. I want to create a pipe too, which will format the keys in my config object as such:

dateOfBirth >> Date of Birth

I am calling it, FormatTitlePipe. Now, my app.module.ts looks like this:

app.module.ts — Final Stage

Step 2: Create the MultiStepFormComponent

multi-step-form.component.ts — Where the sorcery occurs

Let me briefly shed a light at each building block of our component:

ngOnInit()

  • Here, we shall specify activeStepIndex, so that we can redirect user to specific step, in case we want to add save as draft and resume features.
  • We shall have currentFormContent, which simply holds the data of each step. This will have config for each of the forms.
  • We shall have a masterForm, which we desire to hold various forms, as an array of FormGroup . We will iterate over this array in HTML template, to generate required number of forms.
  • We shall have formFields, which we desire to hold the key strings of each config form object, as an array of array of strings. We will iterate over this array to generate required form fields, per form.

buildForm() & getValidators()

Here, I am using the FormBuilder service to create FormGroups with pre defined FormControls and Validators. As you can see quite clearly, the buildForm method along with getValidators, is undertaking this task to eventually return a key as such:

otp: [‘’, [Validators.required, Validators.minLength(4)]]

If we want to use single validator in each field, we can simply pass it as single entity. Here, though, I am using an array of validators even if we need none or one. It makes my job as a developer simpler, since I can use either syntax provided by Angular. Feel free to initialise these FormControls as per their expected data types.

getValidationMessage()

This method maps error type to error message in the config object, and returns that message, to be displayed in form template.

goToStep() & setFormPreview()

These methods are quite straight-forward, which would hide inactive forms and show the active one, as well as, set preview for all fields before submitting. setFormPreview() also aggregates different form values into one single object, which we intend to POST.

Note that I am providing config object via Input and emitting out the final aggregate form object via Output. This helps keep my component reusable, and avoid side-effects.

Step 3: Approaching the finish line, with MultiStepFormComponent template

multi-step-form.component.html

I have used *ngSwitch and *ngSwitchCase structural directives to conditionally display one of many input types, or textarea or dropdown menu, based upon the type of each field from config object. You can further split this component into two: one to generate the outer forms, and another to generate the inner form fields per form.

The important thing to note here is, that every form requires a formGroup associated with it, so that it’s form fields with the attribute formControlName would associate with it. This would help us in marking a form at each step as INVALID, if any of its fields are invalid.

Hence, I have disabled the next button, if user is at last step, or the form he/ she is currently on, is INVALID. Apart from disabling next button, I went ahead and wrote a <p> tag, which will show up, as soon as the field associated with it gets touched or dirty and has an error. You can further enhance this to create this error message <p> tag only for fields, which are configured with any validations (via config object).

Concluding with Final Words

As must be evident through this demo, it is quite easy to configure and modify one-time-setup code to suit the ever changing requirements of UI. All the methods can be easily unit tested as your application scales. Plus, you can make use of FormArrays to add fields dynamically to your form component. Possibilities are endless, and Angular’s Reactive Forms provide you with the flexibility for your use case.

All said and done, my affinity towards Angular as a front-end framework might reflect unabashed in this article of mine. Although, I am always open to learn highlighting features and offerings of all front-end libraries and frameworks. And by no means, am I an expert in navigating vast terrains of Angular.

Feel free to play with and fork the following stackblitz demo. I shall be most thrilled in discussing and improving this small demo, and will appreciate every kind of feedback, if you are so generous as to leave one.

https://stackblitz.com/edit/angular-multi-step-reactive-form?embed=1&file=src/app/constants/multi-step-form.ts

--

--

Harshdeep Bilaiya

Professional Web UI Developer and technology enthusiast. Thank heavens for JavaScript!