Reactive Forms in Angular
Just a couple days ago, we’ve updated our article on Template-driven Forms in Angular, as the APIs have changed for the better in the second release candidate. We think it’s time to talk about model-driven and reactive forms on our blog. This article builds on top of the knowledge we’ve gained in our previous article, so you might want to take a look at it in case you haven’t yet.
The goal of reactive/model-driven forms
In addition, it’s important to understand that when building reactive/model-driven forms, Angular doesn’t magically create the templates for us. So it’s not that we just create the form model and then all of a sudden we have DOM generated in our app that renders the form. Reactive forms are more like an addition to template-driven forms, although, depending on what we’re doing some things can be left out here and there (e.g. validators on DOM elements etc.).
FormGroup and FormControl
Let’s start off again with the same form we used in our previous article, a form to register a new user on a platform:
Nothing special going on here. We simply ask for a firstname, a lastname and some address information. Now, to make this form model-driven, what we need to do is to create a form model that represents that DOM structure in our component. One way to do that is to use the low level APIs for
FormGroup always represents a set of
FormControls. In fact, a form is always a
FormGroup. Let’s create the corresponding form model for our template:
Uff, that looks quite wordy! We’ll fix that in a second but let’s first discuss what happened. We created a component property
registerForm which represents the
FormGroup, which is our form. For each field in the form, we’ve created a
FormControl and what we can’t see here, is that a
FormControl takes a string as first argument in case we want to prefill the form control with a default value.
Another nice thing to notice is that
FormGroup’s can be nested.
address is also a collection of form controls, even though it doesn’t show up in the DOM (yet). We’ll see in a second why that is.
Okay, now that we’ve created our first form model, let’s associate it to our template.
Binding forms using formGroup, formGroupName and formControlName
Currently, there’s nothing in our code that tells Angular that our form model is responsible for the form template. We need to associate the model to our form, and we can do that using the
formGroup directive, which takes an expression that evaluates to a
In order to use that directive we need to import the
ReactiveFormsModule into our application module:
Now we can go ahead and use
formGroup to connect the model with the form template:
Great, the next thing we need to do is to associate the form controls to the model as well, because there’s nothing that tells Angular “Hey these form controls here belong to your form control models!”.
This is where the
formControlName directive comes into play. It’s pretty much the equivalent of an
name attribute combination in template-driven forms. Each form control gets a
formControlName directive applied so we can register the controls on the outer form:
And last but not least, since
address is created as a
FormGroup as well, we can associate a group of form controls to that model using
formGroupName. However, we need a surrounding element for that, otherwise there’s no place where we can apply that directive. Let’s surround the address fields with a
<fieldset> element and apply
It’s time to make our code a bit more readable and replace
As mentioned earlier, the creation of our form model looks quite wordy as we have to call
new FormGroup() and
new FormControl several times to construct the model. Luckily, we’ve used rather low level APIs and we can use a higher level API that makes this task a bit more pleasant.
FormBuilder is like a factory that creates
FormControl’s for us. All we need to do is to import it and us its
.group() method like this:
This looks way better! Let’s recap really quick what happened:
- We imported
- We injected it into
- We created the form model using
- We haven’t done any changes on the template
Now that we have the form model set up, we can start adding validators to our form controls. There are different ways to add them, we can either add then as directives to the template or to the
FormControl instance in our model.
Let’s say we want to add a validators that makes sure that
lastname is set. Angular comes with a
Validators class that has some common validators built-in. We can import and apply them right away:
FormControl takes a value as first, a synchronous validator as second and an asynchronous validator as third parameter. We can also pass a collection of validators which causes Angular to compose them for us. And all we do here is applying a synchronous validator (
Validators.required) to our form controls.
We can access the validity state of a form control like this:
We get a reference to the form control by traversing the
registerForm instance. If you’re interested in learning how to build a custom validator, you might want to read our article on Custom Validators in Angular.
Forms with a single control
Sometimes, we don’t need a
FormGroup, as our form might only consist of a single form control. Think of a search field that let’s you search for products in an e-commerce application. Technically, we don’t even need a
<form> element for that.
Angular comes with a directive
formControl which doesn’t have to be inside a
formGroup. We can simply add it to a single form control and are ready to go:
The cool thing about form controls in Angular is, that we can listen reactively for changes that are happening to that control. Every form controls exposes an Observable propery
valuesChanges() that we can subscribe to. So in order to get notified about changes in the example above, all we have to do is:
Hopefully this one gave you a better idea of how reactive/model-driven and template-driven forms relate to each other.