Reactive froms in Angular, learn how to create and use them!
Have you ever felt your Angular forms lack control over data and behavior? Like they needed a little more pep in their step? Well, that's where Reactive Forms come in to save the day! When it comes to creating forms in Angular, there are two different approaches: template-driven forms and reactive forms (also known as model-driven forms). In this blog post, we'll be focusing on the latter: Angular Reactive Forms. You will learn what the difference is between template-driven and reactive forms, how to create reactive forms, and when to use them!
Table of content:
What are Angular reactive forms?
Creating reactive forms in Angular
Angular FormControl for reactive forms
Step up your game with Angular FormGroup
What are Angular reactive forms?
Reactive forms are one of the two ways of creating forms in the Angular framework. Reactive forms are created by using the ReactiveFormsModule and its properties for form creation and validation. Besides some templating the bulk of the form logic is created and handled in the TypeScript file of the component, unlike template-driven forms that are mostly built and declared in the HTML template. Reactive forms are based on Observables and the RxJS library.
Reactive forms in Angular provide several benefits over simpler template-driven forms. The main difference lies in the way reactive forms approach data management and change detection of the form values. Reactive forms offer a more flexible and dynamic way of building and managing forms with complex state and behavior.
Unlike template-driven forms, reactive forms provide strong control over form behavior and data, making them ideal for handling complex forms. In addition to improved control over the behavior and data, reactive forms also offer robust validation capabilities. This makes it easier to ensure that the data entered into the form is accurate and complete and when it's not you can easily display messages and block the form from being submitted. Lastly, with reactive forms, you have synchronous access to the data model and it uses observable operators and streams that make the form data immutability, reducing the chances of faulty data.
Creating reactive forms in Angular
Now let's have a look at how you can create reactive forms in Angular. Creating a Reactive Form in Angular is a straightforward process, with each step building upon the previous one to create a complete form. Let's start by defining and explaining the 4 main building blocks used to create reactive forms in Angular:
FormControl: FormControl is a class that keeps track of the value and validity state of individual form controls. Each input element in a (reactive) form can be represented as a FormControl. It lets you associate a label with a control and maintain the state of the control, such as its value and validation status.
FormGroup: FormGroup is a collection of FormControl instances grouped together. A FormGroup allows you to validate the entire form in one go. You can think of a FormGroup as a form element that wraps multiple FormControls into a single easy-to-manage object. The FormGroup tracks the value and validity state of all its FormControl instances.
FormBuilder: FormBuilder is an Angular service that you can use to create FormControl and FormGroup instances in a more concise and compact way. Instead of manually creating each instance, you can use the FormBuilder service to create all of them for you.
FormArray: FormArray is a way to track the value and validity state of multiple FormControl instances in a single structure. It can be used to dynamically add or remove FormControl instances in a form.
Now that you have a better understanding of the 4 building blocks of Angular reactive forms, let's have a look at how you can create forms with these 'building blocks'. We will start with a simple example only using FormControls and expand on that to build a more complex form with different validations and fields.
Angular FormControl for reactive forms
First, you need to import the ReactiveFormsModule into our Angular project. This can be done in the AppModule file as follows:
Next, you define the form model using the FormControl class in the component class. This form model serves as the blueprint for your form and allows you to control its behavior and data. Here's a really simple example with just FormConrol:
Once the Form Model is in place (in this case just a simple and single control), you can create the form inside the HTML template and bind it to the Form Model using the [formControl] directive. This binding ensures that any updates to the form data are reflected in the template, and any user inputs are captured by the Form Model.
And that's it! With just a few lines of code, you've created a simple, yet functional, reactive form in Angular. Of course, there is a lot more to it and you can expand upon this quite a lot, so let's not waste any time and keep on building and learning. An Angular FormControl has two properties you can set when you initialize it, the initial value and a configuration object. Adding an initial value to the form control is pretty straightforward, you just replace the empty string with some value like this:
nameControl = newFormControl('Some initial value');
The second property is an object that itself has 4 properties that can be set to enhance your form control.
validators: You can specify one or more validators to validate the value of your form control. There are built-in validators exposed by the Angular Validators class or you can write your own validator function. A full list of the built-in validators can be found here: Angular built-in validators, for more information on building your own validators, click here and learn everything you need to know
asyncValidators: Similar to validators, only these run asynchronous validation methods. They are used when you need to make an API call or perform complex validation logic. An example would be to check if an email is already registered in your database.
updateOn: By default, this property is set to 'change', Angular updates the form control every time the user changes its value. You can change this behavior by setting the updateOn property to 'blur' or 'submit' to only update the form control when the user leaves the input or submits the form.
nonNullable: When you use an initial on your form control and this property is set to true, the initial value will also serve as the default value. When it's not set or set to false, the default value will be null. So when you reset the form control the value goes to null even if it has an initial value. If the value is true the initial value is used again when you reset the form control.
Here is an example of how you can use FormControl with an initial value and the configuration object to configure the form control with validators and specific change detection behavior. In the example below I also added a simple async validator to check if the input is equal to 'abc', if it is, the form is not valid:
Okay great! Now you have a form control that is supercharged with some validations, specific change detection, and initialization behavior. Lastly, let's have a look at some common methods and getters on the FormControl class that can be used to further enhance or control your form and communicate information to the end user.
Methods:
setValue(value: any, options: { onlySelf?: boolean; emitEvent?: boolean; } = {}):
updates the value of the controlpatchValue(value: any, options: { onlySelf?: boolean; emitEvent?: boolean; } = {}):
patch the
value of the controlreset(value: any = undefined, options: { onlySelf?: boolean; emitEvent?: boolean; } = {}):
resets the control to its initial or provided valueregisterOnChange(fn: Function):
registers a callback function that will be called when the value of the control changesregisterOnTouched(fn: Function):
registers a callback function that will be called when the control is marked as "touched"
Getters:
value:
gets the current value of the controlvalid:
returnstrue
if the control is valid,false
otherwiseinvalid:
returnstrue
if the control is invalid,false
otherwisepending:
returnstrue
if the control is pending validation,false
otherwisedisabled:
returnstrue
if the control is disabled,false
otherwiseenabled:
returnstrue
if the control is enabled,false
otherwiseerrors:
returns an object with validation error information, ornull
if the control is validpristine:
returnstrue
if the control has not been interacted with,false
otherwisetouched:
returnstrue
if the control has been marked as "touched",false
otherwisestatus:
returns a string representation of the control's validation status (VALID
,INVALID
,PENDING
, orDISABLED
)
Step up your game with Angular FormGroup
The Angular form control is nice and lets you build reactive forms with advanced validation, but when you have more complex forms there is a better way. Let's say you have a form with a lot of fields and some field validations depend on the value of other fields or you want to check the validation state of the entire form, things like this become quite messy with separate form controls. So when your forms grow it's better to use FormGroup.
The FormGroup in Angular is a way to group multiple FormControl objects together in a single unit. It allows you to track and manage the values and validation status of multiple form fields at once. You create the Angular FormGroup within your component class and bind it to your HTML template using the formGroup directive.
One of the main benefits of Angular FormGroup is that it allows you to perform cross-field validation, meaning you can validate multiple form fields at once based on the values of other fields. It also exposes a valid property for the validity of the entire field, making it easy to disable your submission button for example.
As you can see in the example, creating a FormGroup is easy and straightforward. It's just an object taking in FormControls to configure it. After you created it you can just use the formGroup directive and bind it in your HTML template.
The FormGroup also exposes several properties and methods to manage the form. Below are some examples and the most used properties and methods for FormGroup.
Properties
value
: An object containing the current values of all the controls in the form group.status
: A string indicating the current validation status of the form group (VALID
,INVALID
,PENDING
, orDISABLED
).controls
: An object containing all the child controls of the form group.validator
: A synchronous validator function that validates the entire form group.asyncValidator
: An asynchronous validator function that validates the entire form group.
Methods
get()
: Returns aFormControl
orFormGroup
instance for the given control name/path.addControl()
: Adds a form control to the form group.removeControl()
: Removes a form control from the form group.contains()
: Checks whether a form control is present in the form group.setValue()
: Sets the value of the form group. This method replaces the entire value object, so all child controls must be specified.patchValue()
: Sets the value of one or more controls in the form group. This method allows you to specify a subset of the controls to update, without affecting the rest of the controls.reset()
: Resets the form group to its initial state. This method sets the value of all child controls to their initial values.
Reduce boilerplate code with Angular FormBuilder
The Angular FormGroup and FormControls are all you need to build advanced reactive forms in Angular. But as you might have noticed it requires quite some boilerplate to create. This is where the FormBuilder comes to the rescue! The FormBuilder is a service exposed by Angular to create FormGroups and controls with less boilerplate code. Using the FormBuilder also makes your code easier to test with a unit test.
You can use the Angular FormBuilder by injecting it into your component class with dependency injection. Next, you can use this syntax to create your form group and form controls:
Conclusion
There are many ways how you can build forms in Angular. You can just use inputs and divs, go for a simple HTML form, or create a template-driven or even a reactive form. Often you might be tempted to just create simple inputs, but when you want to step up your game and do advanced validations, show error messages to users, and disable fields and buttons inside your forms your best bet is to go for reactive forms. Compose and handle the logic of your files in the typescript files and reduce boilerplate code with the Angular form builder. Make sure everything is testable and the state of your forms stays manageable and in sync with your template. Quick updates with patch value and other cool methods, all of this and more is what using reactive forms will bring to your applications.