Reactive Forms in Angular The basics
Muhammad Muhktar Musa
Posted on September 27, 2021
Introduction
Angular uses reactive methods to process and manage form. It is easy to use reactive forms to capture user inputs events, validate the inputs and create form models in large angular applications. This enables the tracking of data and changes in the form model in all parts of the application.
Reactive Form model setup
Reactive forms provide a model-driven approach to handling form inputs whose values change over time. It uses an explicit and immutable approach to manage the form at a given point in time and it is built around observable streams.
Adding a basic form control
- Register the reactive form module in the app module declaration of an angular app. This module declares a reactive form directives needed to use the reactive form
- Generate a new form control instance and save in the component class
- Register the form control in the template.
Let us take a look at how to implement the above. To use the reactive form controls we need to import ReactiveFormsModule from @angular/forms package and add it to the NgModule imports array
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports:[
ReactiveFormsModule],
});
Next is to generate a form control. To register a single form control, we import the form control class and create a new instance of FormControl that is saved as a class property.
import { FormControl } from '@angular/forms';
@Component({
selector: 'app-cart',
templateUrl: './cart.component.html',
styleUrls: ['./cart.component.css']
})
export class CartComponent implements OnInit {
name = new FormControl('');
}
By creating this control in the component class, immediate access is gotten to listen for, update and validate the state of the form input.
To register the control in the template, we can add a label to the template
<label>Name:
<input type="text" [formControl]="name">
</label>
Displaying a form control value can be achieved through value changes from observables where changes are listened for in the template using AsyncPipe
or in the component class using a subscribe method. It can also be achieved with the value property which gives a snapshot of the current value.
Let us take an example of how to display the value using interpolation in the template.
<label>Name:
<input type="text" [formControl]="name">
<p>
value: {{name.value}}
</p>
</label>
The displayed value changes as the form control element is updated. Reactive forms have methods they use to change a control value programmatically. This gives flexibility to update the value without user interaction. A form control instance provides a setValue()
method that updates the value of the form control and validates the structure of the value provided against the control structure. To update the name we can use the setValue method as below
upDateName() {
this.name.setValue('Kings');
}
Update the template with a button to simulate name update
<label>Name:
<input type="text" [formControl]="name">
<p>
value: {{name.value}}
</p>
</label>
<button (click)="upDateName()">Update Name</button>
The form model is the source of truth for the control. When the button is clicked the value of the input is changed within the component class overriding its current value.
Grouping form controls
Forms typically contain several related controls. Reactive forms provide two ways of grouping multiple related controls into a single input form.
- A form group that defines a dynamic form with a fixed set of controls that can be managed together.
- A form array that defines a dynamic form where controls can be added or removed at run time. A single form control instance gives control over a single input field while a form group instance tracks the form state of a group of form control instances. let us take a look at this in play. Import the form group class from the angular packages
import { FormControl, FormGroup } from '@angular/forms';
Create a form group instance, associate the form group model and view then save the data.
profileForm = new FormGroup({
firstName: new FormControl(''),
lastname: new FormControl('')
});
Associate the form group model and view in the template
<form [formGroup]="profileForm ">
<label>First Name:
<input type="text" formControlName="firstName">
<p>
value: {{name.value}}
</p>
</label>
<label>Last Name:
<input type="text" formControlName="lastName">
<p>
value: {{name.value}}
</p>
</label>
</form>
To save the form data the form group directive listens for the submit event emitted by the form element which can be bind to a callback function. let us add an ngSubmit event listener to the form tag with the onSubmit()
callback method.
<form [formGroup]="profileForm " (ngSubmit)="onSubmit()">
<label>First Name:
<input type="text" formControlName="firstName">
<p>
value: {{name.value}}
</p>
</label>
<label>Last Name:
<input type="text" formControlName="lastName">
<p>
value: {{name.value}}
</p>
</label>
</form>
add the method to the class
onSubmit() {
console.log(this.profileForm.value);
}
Use a button element to add a button to the form to trigger the form submission
<button type="submit" [disabled]="profileForm.valid"></button>
Creating nested form groups
Form groups can accept individual form control instances and other form group instances as children. This makes composing complex form models easier to maintain and logically grouped together. Let us create and take a look at a complex form.
profileForm = new FormGroup({
firstName: new FormControl(''),
lastname: new FormControl(''),
address: new FormGroup({
street: new FormControl(''),
city: new FormControl(''),
state: new FormControl(''),
zip: new FormControl('')
})
});
Group the nested form in a template
<div formGroupName="address">
<h5>Address</h5>
<label>City:
<input type="text" formControlName="city">
<p>
value: {{city.value}}
</p>
</label>
<label>Street Name:
<input type="text" formControlName="street">
<p>
value: {{street.value}}
</p>
</label>
<label>State Name:
<input type="text" formControlName="state">
<p>
value: {{state.value}}
</p>
</label>
<label>Zip:
<input type="text" formControlName="zip">
<p>
value: {{zip.value}}
</p>
</label>
</div>
The updateProfile()
method can be used to update the firstName and street of the user
updateProfile() {
this.profileForm.patchValue({
firstName: 'jules',
address: {
street: '234 jules miles street'
}
})
}
simulate an update by adding a button to the user profile
<button (click)="updateProfile()">update profile</button>
Generating controls using the formbuilder service
To generate controls using the formbuilder service, we need to import the formbuilder class then inject the formbuilder service and then generate the form contents.
import the formbuilder class
import { FormBuilder} from '@angular/forms';
inject the formbuilder service
constructor( private fb: FormBuilder) { }
generate the controls
profileForm = this.fb.group({
id: [''],
teacherIds: [''],
studentIds: [''],
});
Validating form input
Form validation is used to ensure that user input is complete and correct. To achieve this we import a validator function in the form component
import { Validators} from '@angular/forms'
Add logic and validators to the form fields required
profileForm = this.fb.group({
id: ['', validators.required],
teacherIds: [''],
studentIds: [''],
});
Add the validator to the template
<input placeholder="id" formControlName="id" required />
Display current status of form by using interpolation
<p> Form status: {{profileForm.status}} </p>
We can see from our discussion that building a form using the reactive form approach makes managing immutable form input data at a given point much easy.
Posted on September 27, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.