Reactive forms
This article takes a look at Angular’s ReactiveFormsModule and the benefits it brings when working with forms, user input, form validation and more.
Limitations of ngModel
In Angular, there are several ways to reactively work with inputs and their values. A most simple approach would be to create a property containing the value, set the value of an input and listen for changes:
public username = '';
<input [value]="username" (input)="username = $event" />
or, using ngModel:
<input [(ngModel)]="username" />
This works fine if you have, for example, a simple text input to filter a list of items in an array. But when you need to add more functionalities, such as form validation, things can get out of hand quickly.
Template vs model driven
They are more powerful, easier to use and encourage a better separation between view and business logic. The Reactive approach removes validation logic from the template, keeping the templates cleaner. support better more advanced use cases via its Observable-based API.
Use reactive forms
Angular provides a comprehensive way for working with forms, using their ReactiveForms module. Make sure your module imports the ReactiveFormsModule in your (feature) module.
Please refer to the Angular guide about reactive forms for more in-depth information.
Since Angular 14, forms are now fully type-safe. The examples in this article will not define types explicitly, and will therefor also work in older versions, although the TS compiler will not be able to verify type safety. To get a quick overview of how types forms work, check out this video.
Building forms
When using reactive forms, you will be using the FormControl, FormArray and FormGroup extensively. For a short overview of these building blocks, refer to this section of the docs.
Let’s say you have a user object, with the following interface:
interface User {
name: string;
active: boolean;
interests: Array<string>;
address: {
street: string;
zipCode: string;
city: string;
};
}
Using constructors
The corresponding form group constructor would look something like this:
const form = new FormGroup({
firstName: new FormControl(''),
active: new FormControl(false),
interests: new FormControl([]),
address: new FormGroup({
street: new FormControl(''),
zipCode: new FormControl(''),
city: new FormControl(''),
}),
});
Using FormBuilder class
An even better approach is to use the FormBuilder:
// Inject the FormBuilder
const builder = inject(FormBuilder);
// Create new form
const form = builder.group({
firstName: '',
active: false,
interests: [],
address: builder.group({
street: '',
zipCode: '',
city: '',
}),
});
Form validation
Angular comes with several built-in form validation functions, available in the Validator class.
Please refer to the Angular guide about form validation for more in-depth information.
Custom validation
We recommend writing your own form validators in a class. You can extend the Validators class, or write your own class.
Async form validation
Create custom inputs
This section will show how you can easily create custom components that implement support for ReactiveForms. This will come in handy when you want to create high-order inputs that support working with [formControl]. There are several reasons to create a custom component:
For lists of entities in your state, that you want to be able to select with a dropdown (one or many).
For inputs that have unique interfaces.
Caveats
Avoid using formControl as @Input() name
When using model driven forms, you connect your FormControl to an input using the [formControl] directive. This add some built-in functionality. Creating a custom component that implements an @Input with the same name, some things will conflict. You will get errors that say something about ngDefaultControl.
Difference between value and getRawValue()
To get the current value of a form, you can access the value property, or call getValue() method.
Disabled controls
When using model driven forms, you have to disable controls by calling the .disable() method on the FormControl. Note that these controls will not show up in your form’s value. The key will not be present in the value object.