Skip to main content

Form

ReactiveFormsModule

Preface Reminder

ReactiveFormsModule is a module provided by Angular to create and manage Reactive Forms, allowing us to control and update the values and validation state of a form directly from the component class. Dynamic forms using ReactiveFormsModule can dynamically add or remove form controls based on business requirements.

1. Preparation

First, make sure that ReactiveFormsModule has been imported into your Angular module. Step 1: Import ReactiveFormsModule in the module Add ReactiveFormsModule to your module file app.module.ts to ensure that your components can use the responsive forms functionality.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ReactiveFormsModule // import ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

2. Creating a Dynamic Form Component

We will create a simple dynamic form component that allows users to dynamically add and remove form controls. Step 2: Create Component Create a new component using Angular CLI with the following command:

ng generate component dynamic-form

Step 3: Implementing Dynamic Form Logic In the generated component file, use FormBuilder and FormArray to create dynamic forms.

  1. **FormBuilder **: used to simplify the form creation process. 2. **FormArray **: used to create the dynamic form.
  2. FormArray: used to manage an array of form controls for dynamic forms. In dynamic-form.component.ts:
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms';

@Component({
selector: 'app-dynamic-form',
templateUrl: './dynamic-form.component.html',
styleUrls: ['./dynamic-form.component.css']
})
export class DynamicFormComponent {
myForm: FormGroup;

constructor(private fb: FormBuilder) {
// Initialize the form and create an empty FormArray
this.myForm = this.fb.group({
items: this.fb.array([]) // items is a FormArray
});
}

// Get the items array
get items(): FormArray {
return this.myForm.get('items') as FormArray;
}
}

// Dynamically add new controls
addItem(): void {
const itemForm = this.fb.group({
name: ['', Validators.required], // Name control
quantity: [1, [Validators.required, Validators.min(1)]] // Quantity control, minimum value is 1
});
this.items.push(itemForm); // Add to FormArray
}

// Dynamically remove controls
removeItem(index: number): void {
this.items.removeAt(index); // Remove controls from FormArray
}

3. Create the HTML template for the dynamic form

In dynamic-form.component.html:

<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<div formArrayName="items">
<div *ngFor="let item of items.controls; let i = index" [formGroupName]="i" style="margin-bottom: 1em;">
<label>
Name:
<input formControlName="name" placeholder="Item name">
</label>

<label>
Quantity:
<input type="number" formControlName="quantity" placeholder="Quantity">
</label>

<button type="button" (click)="removeItem(i)">Remove</button>
</div>
</div>

<button type="button" (click)="addItem()">Add Item</button>
<button type="submit" [disabled]="myForm.invalid">Submit</button>
</form>

4. Run the code and test

Test steps:

  1. Open the Angular application and navigate to the route where the dynamic form component is located.

  2. Click the "Add Item" button to add a new input box for the user to enter the name and quantity of the item.

  3. Fill in the form content and click "Submit" to submit the form.

  4. Check the console and the form value will be printed out.

Summary

  • FormArray allows us to create an array of controls that can be added or deleted dynamically, which is very suitable for dynamic forms.

  • FormBuilder makes form creation more concise.

  • Validators is used to set validation conditions for form controls to ensure that user input meets the requirements.

FormGroup

In Angular, FormGroup is a class used to group multiple form controls into a group, which is convenient for managing and validating these controls. A FormGroup can contain multiple FormControl and treat them as a whole, which is very suitable for complex form structures.

Below is the detailed code and steps on how to create a form using FormGroup.

1. Preparation

Make sure that ReactiveFormsModule has been imported in the Angular project to use the responsive form function.

Import ReactiveFormsModule in app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
ReactiveFormsModule // Import ReactiveFormsModule to support responsive forms
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

2. Create a form component

Create a new component to implement the form functionality. Suppose we create a registration form component with "username", "password" and "email" fields.

Use Angular CLI to create a new component

ng generate component registration-form

3. Create FormGroup in the component

In registration-form.component.ts, create a form using FormGroup and FormControl and initialize these controls in the constructor.

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
selector: 'app-registration-form',
templateUrl: './registration-form.component.html',
styleUrls: ['./registration-form.component.css']
})
export class RegistrationFormComponent implements OnInit {
registrationForm: FormGroup;

constructor() {
// Initialize FormGroup and define each form control
this.registrationForm = new FormGroup({
username: new FormControl('', [Validators.required, Validators.minLength(3)]), // Username control, required, at least 3 characters
password: new FormControl('', [Validators.required, Validators.minLength(6)]), // Password control, required, at least 6 characters
email: new FormControl('', [Validators.required, Validators.email]) // Email control, required, must conform to email format
});
}

ngOnInit(): void {}

onSubmit(): void {
if (this.registrationForm.valid) {
// Process valid form data
console.log('Form submitted:', this.registrationForm.value);
} else {
// Prompt error when the form is invalid
console.log('Form is invalid');
}
}
}

4. Create the HTML template for the form

In the component template file registration-form.component.html, use Angular's formGroup directive to bind the form to registrationForm and add input boxes for each control.

<form [formGroup]="registrationForm" (ngSubmit)="onSubmit()">
<div>
<label for="username">Username:</label>
<input id="username" formControlName="username">
<div *ngIf="registrationForm.get('username')?.invalid && registrationForm.get('username')?.touched">
<small *ngIf="registrationForm.get('username')?.errors?.['required']">Username is required.</small>
<small *ngIf="registrationForm.get('username')?.errors?.['minlength']">Username must be at least 3 characters long.</small>
</div>
</div>

<div>
<label for="password">Password:</label>
<input type="password" id="password" formControlName="password">
<div *ngIf="registrationForm.get('password')?.invalid && registrationForm.get('password')?.touched">
<small *ngIf="registrationForm.get('password')?.errors?.['required']">Password is required.</small>
<small *ngIf="registrationForm.get('password')?.errors?.['minlength']">Password must be at least 6 characters long.</small>
</div>
</div>

<div>
<label for="email">Email:</label>
<input id="email" formControlName="email">
<div *ngIf="registrationForm.get('email')?.invalid && registrationForm.get('email')?.touched">
<small *ngIf="registrationForm.get('email')?.errors?.['required']">Email is required.</small>
<small *ngIf="registrationForm.get('email')?.errors?.['email']">Please enter a valid email address.</small>
</div>
</div>

<button type="submit" [disabled]="registrationForm.invalid">Register</button>
</form>

5. Validate and submit the form

In the template, each input box has a validation prompt message. The corresponding error prompt is only displayed when the control is touched and the validation fails. After the user fills in the form and passes the validation, the form can be submitted. The form data will be printed out in the console.

Summary

  • FormGroup: Used to combine multiple FormControl into a group for unified management and validation.

  • FormControl: Represents a single form control, used to receive the value entered by the user.

  • Validators: Provides some commonly used validators, such as required, minLength, email, etc.

Final code

registration-form.component.ts

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
selector: 'app-registration-form',
templateUrl: './registration-form.component.html',
styleUrls: ['./registration-form.component.css']
})
export class RegistrationFormComponent implements OnInit {
registrationForm: FormGroup;

constructor() {
this.registrationForm = new FormGroup({
username: new FormControl('', [Validators.required, Validators.minLength(3)]),
password: new FormControl('', [Validators.required, Validators.minLength(6)]),
email: new FormControl('', [Validators.required, Validators.email])
});
}

ngOnInit(): void {}

onSubmit(): void {
if (this.registrationForm.valid) {
console.log('Form submitted:', this.registrationForm.value);
} else {
console.log('Form is invalid');
}
}
}

registration-form.component.html

<form [formGroup]="registrationForm" (ngSubmit)="onSubmit()">
<div>
<label for="username">Username:</label>
<input id="username" formControlName="username">
<div *ngIf="registrationForm.get('username')?.invalid && registrationForm.get('username')?.touched">
<small *ngIf="registrationForm.get('username')?.errors?.['required']">Username is required.</small>
<small *ngIf="registrationForm.get('username')?.errors?.['minlength']">Username must be at least 3 characters long.</small>
</div>
</div>

<div>
<label for="password">Password:</label>
<input type="password" id="password" formControlName="password">
<div *ngIf="registrationForm.get('password')?.invalid && registrationForm.get('password')?.touched">
<small *ngIf="registrationForm.get('password')?.errors?.['required']">Password is required.</small>
<small *ngIf="registrationForm.get('password')?.errors?.['minlength']">Password must be at least 6 characters long.</small>
</div>
</div>

<div>
<label for="email">Email:</label>
<input id="email" formControlName="email">
<div *ngIf="registrationForm.get('email')?.invalid && registrationForm.get('email')?.touched">
<small *ngIf="registrationForm.get('email')?.errors?.['required']">Email is required.</small>
<small *ngIf="registrationForm.get('email')?.errors?.['email']">Please enter a valid email address.</small>
</div>
</div>

<button type="submit" [disabled]="registrationForm.invalid">Register</button>
</form>

Form processing

In Angular, it is an important part of building interactive user interfaces. In order to effectively manage and validate user input, Angular provides two tools, FormBuilder and Validators. The following is a detailed explanation of them.

1. FormBuilder

FormBuilder is a service in Angular that simplifies the process of creating form controls. It makes the creation of forms more concise and intuitive. FormBuilder provides some methods that can be used to generate form controls, form groups, and form arrays.

Function and use

  • Creating form controls: Using FormBuilder, you can easily create form controls and groups.

  • Better readability: By using FormBuilder, the structure of the form can be clearer, and the code is easier to understand and maintain.

Sample code

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
selector: 'app-my-form',
templateUrl: './my-form.component.html',
})
export class MyFormComponent implements OnInit {
myForm: FormGroup;

constructor(private fb: FormBuilder) { } // Inject FormBuilder

ngOnInit(): void {
// Create a form using FormBuilder
this.myForm = this.fb.group({
username: ['', Validators.required], // Create controls and add validators
password: ['', [Validators.required, Validators.minLength(6)]]
});
}

onSubmit(): void {
if (this.myForm.valid) {
// Process a valid form
console.log(this.myForm.value);
} else {
// Process an invalid form
console.log('Form is invalid');
}
}
}

2. Validators

Validators is a tool class that provides common form validators. It provides a series of static methods that can be used to check whether the input value of the form control meets specific rules.

Function and usage

  • Common validators: Validators provides some common validators, such as required, minLength, maxLength, pattern, etc.

  • Custom validators: In addition to using built-in validators, developers can also define custom validators to meet specific business needs.

Sample code

import { AbstractControl, ValidationErrors } from '@angular/forms';

// Custom validator example
export function customValidator(control: AbstractControl): ValidationErrors | null {
const forbidden = /admin/.test(control.value); // Check if the input value contains "admin"
return forbidden ? { forbiddenName: { value: control.value } } : null; // Return validation result
}

3. Comprehensive example

Here is a complete example with custom validators, FormBuilder, and Validators:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { customValidator } from './custom-validator'; // Importing a custom validator

@Component({
selector: 'app-registration-form',
templateUrl: './registration-form.component.html',
})
export class RegistrationFormComponent implements OnInit {
registrationForm: FormGroup;

constructor(private fb: FormBuilder) { }

ngOnInit(): void {
this.registrationForm = this.fb.group({
username: ['', [Validators.required, customValidator]], // Use custom validator
password: ['', [Validators.required, Validators.minLength(6)]],
email: ['', [Validators.required, Validators.email]] // Use built-in email validator
});
}

onSubmit(): void {
if (this.registrationForm.valid) {
console.log(this.registrationForm.value);
} else {
console.log('Form is invalid');
}
}
}

Summary

  • FormBuilder: Used to simplify the creation of form controls, making the form structure clearer and easier to maintain.

  • Validators: Provides built-in common validators and supports custom validators to help developers effectively validate user input.

  • Custom validators: Through custom validators, developers can extend validation logic according to business needs and enhance the flexibility and applicability of forms.

By combining FormBuilder and Validators, Angular provides a powerful and flexible way to handle form input and validation to ensure the validity of user data.

Custom validation method

FormBuilder is an auxiliary tool class provided by Angular to simplify the creation of form controls in form components.

Validators is a tool class that provides common validators for performing validation in form controls.

import { FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms'

constructor(private fb: FormBuilder) { }

valiDataForm: FormGroup = this.fb.group({
userName: [
'',
[Validators.required, Validators.maxLength(18), Validators.minLength(6)],
],
password: ['', [this.passwordVal]],
phone: ['', [Validators.required, this.phoneVal]],
});

passwordVal(password: FormControl): object {
const value = password.value || '';
if (!value) {
return { msg: 'Please enter password' };
} else {
const valid = value.match(/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/);
return valid ? {} : { msg: 'The password must contain at least 6 digits or English characters, and the length must be 6-20' };
}
}

phoneVal(phone: FormControl): object {
const value = phone.value || '';
if (!value) return { msg: 'Please enter your phone number' };
const valid = value.match(/[0-9]{11}/);
return valid ? {} : { msg: 'The contact number must be 11 digits' };
}

subFormFunction() {
console.log(this.valiDataForm.get('userName')?.value);
console.log(this.valiDataForm.get('password')?.value);
console.log(this.valiDataForm.get('phone')?.value);
}

Front-end

<form [formGroup]="valiDataForm">

<label>
Account: <input type="text" formControlName="userName" />
</label>

<p *ngIf="valiDataForm.get('userName')?.errors?.['required']">Please enter account number</p>
<p *ngIf="valiDataForm.get('userName')?.errors?.['minlength']?.requiredLength
||valiDataForm.get('userName')?.errors?.['maxlength']?.requiredLength">The account length is between 6-18 digits</p>

<br />

<label>
Password: <input type="text" formControlName="password" />
</label>

<p *ngIf="valiDataForm.get('password')?.errors?.['msg']">{{ valiDataForm.get('password')?.errors?.['msg'] }}</p>

<br />

<label>
Phone Number: <input type="text" formControlName="phone" />
</label>

<p *ngIf="valiDataForm.get('phone')?.errors?.['msg']">{{ valiDataForm.get('phone')?.errors?.['msg'] }}</p>


<br />

<button (click)="subFormFunction()">
Submit
</button>
</form>