Angulars ReactiveForms give us immense capabilities with its robust API, but the learning curve can be a bit steep from plain old template-driven forms that many are used to. This quick guide will explain Angular’s form elements and how to combine them, nest them, and dynamically create them in almost any scenario.
AbstractControl
First, it’s important to know about AbstractControl, the class extended across most of the form elements we’ll be working with. It has multiple properties that manage everything from the validity state to what the parent element may be, and methods that allow us to mark the state of the control(touched, untouched, dirty, etc), enable/disable the control, get the value, set the value, etc. There’s a lot going in this class, so it’s handy to have it’s documentation available to refer to:
FormControl
The basic element of building Angular forms is the FormControl. This is a class that represents that input element on a page with a name value you’re likely used to seeing. Any piece of information we want to collect in a form, whether it’s an input, select, dropdown, or custom element needs to have a representative FormControl. The [formControl]
directive is used to bind the input element in the DOM to it’s respective FormControl.
FormControls can be initialized with a value, like ‘your name here’ in the above example, and enabled/disables status, and set any validators necessary.
FormGroups
FormGroup is the class that allows us to group a number of controls together. It also extends the AbstractControl class, meaning we can track the validity and value of all the FormControls in a FormGroup together. This allows us to easily manage our form as a whole. The [formGroup]
directive binds the FormGroup to a DOM element.
FormArray
FormArray is a class that aggregates FormControls into an array, similar to FormGroup creating an object from FormControls. FormArrays can have controls pushed to them or removed from them similar to the way you’d manipulate an array in vanilla JS, and offer us a lot of power and flexibility when creating nested and dynamic forms.
In this example, we’re using an ngFor
loop to iterate through userForm.controls
, because userForm
is a FormArray. In this FormArray are controls, so as we loop through the controls, we need to be sure to use the [formGroup]
directive to bind each iteratee DOM element to it’s respective FormGroup instance.
FormBuilder
Repeatedly typing new FormControl('')
, new FormGroup({})
, and new FormArray([])
can become a bit tedious, especially when creating larger forms. Fortunately Angular has shorthand syntax we can use thanks to the FormBuilder class.
Now that we have an understanding of the pieces we can build forms with, let’s look at building a more complex form example.
Creating and Removing FormControls & FormGroups Dynamically
Let’s say we want to create a form that allows a user to create an unlimited number of users. This form will need to be able to allow a user to add new FormGroups to create additional users as needed, as well as remove FormGroups they don’t want. We use a FormArray to hold a FormGroups of FormControls for each user we want to create. To do this, we can use FormArray methods:
- An
insert
method that takes two parameters, the index at which to insert, and the control to insert. - A
removeAt
method, which takes the index of the control to remove.
Now we have a form that dynamically adds and removes FormGroups to allow the user to create as many users as they would like. With a thorough understanding of FormControl, FormGroup, and FormArray we can create any form structure to meet our data submissions needs.
To see working examples of all the code snippets shown above, take a look at the repo on stackblitz.
https://stackblitz.com/github/tehfedaykin/angular-dynamic-forms