The Form Array is a way to group Form controls in Angular. We can group FormControl in Angular forms in two ways. One is using the FormGroup and the other one is FormArray. The difference is how they implement it. In FormGroup controls becomes a property of the FormGroup. Each control is represented as key-value pair. While in Form Array, the controls become part of an array
Because it is implemented as Array, it makes it easier dynamically add controls.
Angular 8 FormArray is a bit like FormGroup, and it’s used in a very similar way, the difference being that it’s used as an array that envelops around an arbitrary amount of FormControl, FormGroup or even other FormArray instances.FormArray is a class of @angular/forms module.
constructor(controls: AbstractControl[], validatorOrOpts?: ValidatorFn | AbstractControlOptions | ValidatorFn[], asyncValidator?: AsyncValidatorFn | AsyncValidatorFn[])
The controls parameter is required, and it is an array of child controls. Each child control is given the index where it is registered.
The validatorOrOpts is an optional parameter. It is an asynchronous validator function, or an array of such functions, or an AbstractControlOptions object that contains validation functions and the validation trigger.
The asyncValidator is an optional parameter. It is a single async validator or array of async validator functions.
Angular FormArray has two properties.
Type the following command to create a new Angular project.
ng new angforms
Install Bootstrap 4 using the following command.
➜ angforms git:(master) ✗ npm install bootstrap --save
npm WARN axobject-query@2.1.1 requires a peer of eslint@^5 || ^6 but none is installed. You must install peer dependencies yourself.
npm WARN bootstrap@4.4.1 requires a peer of jquery@1.9.1 - 3 but none is installed. You must install peer dependencies yourself.
npm WARN bootstrap@4.4.1 requires a peer of popper.js@^1.16.0 but none is installed. You must install peer dependencies yourself.
+ bootstrap@4.4.1
added 1 package from 2 contributors and audited 19367 packages in 12.498s
14 packages are looking for funding
run `npm fund` for details
found 2 moderate severity vulnerabilities
run `npm audit fix` to fix them, or `npm audit` for details
➜ angforms git:(master) ✗
Now, add the following code inside the angular.json
file.
"styles": [
"src/styles.css",
"./node_modules/bootstrap/dist/css/bootstrap.min.css"
],
Now, write the following code inside the app.module.ts
file.
// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
We have imported ReactiveFormsModule because we are using the Reactive Forms approach and not a template-driven approach.
Creating a form using FormControl, FormGroup, and FormArray are said to be the reactive forms. They use an ng module as ReactiveFormsModule.
For every form control such as text, checkbox, radio button, we need to create an instance of FormControl in our class.
For instance, let’s say we need to create an instance of the name field.
name = new FormControl();
In our HTML template, you can use the following code.
<input [formControl]="name">
Okay, now write the following code inside the app.component.ts
file.
// app.component.ts
import { Component } from '@angular/core';
import { FormControl, FormGroup, FormArray, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
angForm = new FormGroup({
names: new FormArray([
new FormControl('', Validators.required),
new FormControl('', Validators.required),
])
});
}
In the above code, we have imported four angular classes.
FormControl: FormControl is a class that is used to get and set values and validation of the form control such as <input>
and <select>
tag.
FormGroup: FormGroup has a role in tracking the value and validity state of a group of FormControl.
FormArray: FormArray tracks a value and validity state of the array of FormControl, FormGroup, or FormArray instances.
Validators: Validators provides a set of built-in validators that can be used by form controls.
Also, we need to define one Getter method for the names array.
Write the following code inside the app.component.ts file.
// app.component.ts
get names(): FormArray {
return this.angForm.get('names') as FormArray;
}
It will return all the names as FormArray.
Then we have created a FormGroup instance called angForm.
We have passed the FormArray instance, which consists of two FormControls.
We will iterate names array in our UI. Write the following code inside the app.component.html file.
<!-- app.component.html -->
<div formArrayName="names">
<div *ngFor="let name of names.controls; index as idx">
<input [formControlName]="idx" placeholder="Enter a Name">
<label *ngIf="name.invalid" [ngClass] = "'error'"> Name is required. </label>
</div>
</div>
When we submit the form, we can fetch values as given below.
Write the following code inside the app.component.ts file.
// app.component.ts
onFormSubmit(): void {
for(let i = 0; i < this.names.length; i++) {
console.log(this.names.at(i).value);
}
}
On the instance of FormArray, i.e., names, we will call controls that will return an array of FormControl instances. Now to add a form control at run time, we need to use the push() method of FormArray.
To remove from FormArray, we will use removeAt() function and pass the index parameter. We can remove the form control at run time; we need to use the removeAt() method of FormArray.
We are also adding validation while creating FormControl instances.
// app.component.ts
addNameField() {
this.names.push(new FormControl('', Validators.required));
}
deleteNameField(index: number) {
if (this.names.length !== 1) {
this.names.removeAt(index);
}
}
We are also adding validation when adding a new FormControl.
That means a new, dynamically added text field has required validation.
So, our app.component.ts file looks like this.
// app.component.ts
import { Component } from '@angular/core';
import { FormControl, FormGroup, FormArray, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
angForm = new FormGroup({
names: new FormArray([
new FormControl('', Validators.required),
new FormControl('', Validators.required),
])
});
get names(): FormArray {
return this.angForm.get('names') as FormArray;
}
onFormSubmit(): void {
for (let i = 0; i < this.names.length; i++) {
console.log(this.names.at(i).value);
}
}
addNameField() {
this.names.push(new FormControl('', Validators.required));
}
deleteNameField(index: number) {
if (this.names.length !== 1) {
this.names.removeAt(index);
}
console.log(this.names.length);
}
}
Now, write our final code inside the app.component.html file looks like the below.
<!-- app.component.html -->
<div class="container">
<h3>Angular 8 FormArray Example</h3>
<form [formGroup] = "angForm" (ngSubmit)="onFormSubmit()">
<div formArrayName="names">
<div *ngFor="let name of names.controls; index as idx" class="form-group">
<input [formControlName]="idx" placeholder="Enter a Name" class="form-control">
<label *ngIf="name.invalid" [ngClass] = "'error'"> Name is required. </label>
<button type="button" (click)="addNameField()" class="btn btn-success" [ngClass] = "'pad'">Add More Names</button>
<button (click)="deleteNameField(idx)" class="btn btn-danger">Delete</button>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Send</button>
</div>
</form>
</div>
Our CSS code inside the app.component.css file is the following.
.error{
color: red;
font-size: 20px;
margin-top: 10px;
margin-right: 10px;
}
.pad{
margin-right: 10px;
margin-top: 10px;
}
Now, when you start typing your name at a particular textbox, for that specific textbox, the error will disappear.
When the user submits the form, we will get all the dynamically added textbox values.
We can also add or remove the textboxes dynamically, which is really cool.
So, if you want a functionality in which you need to add dynamic fields in Angular 8, then this is your perfect solution.
See the output.
Okay, now let’s see some of the functions of Angular 8 FormArray.
FormArray.at()
Get the AbstractControl at the given index in the array. It is an index in the array to retrieve the control.
FormArray.push()
The push() function inserts a new AbstractControl at the end of the array. We have seen the push() method in our dynamic insert fields example.
FormArray.insert()
The insert() function inserts a new AbstractControl at the given index in the array.
Form.removeAt()
The removeAt() function removes the control at the given index in the array. We have seen the removeAt() method in our dynamic remove fields example.
You can learn more about the functions of FormArray here
This tutorial is the end, if you find it helpful to share it with everyone. Thanks for reading!
#angular #angular8 #developer #webdeveloper