Angular Forms: A Complete Guide

In this tutorial, we will explore Angular Forms - an essential feature in Angular development. We will cover both Template-driven Forms and Reactive Forms, explaining how to create forms, handle form submission, and implement form validation. Additionally, we will discuss form controls, input controls, form arrays, and the Form Builder. By the end of this guide, you will have a comprehensive understanding of Angular Forms and how to effectively use them in your applications.

angular forms complete guide

Introduction

What are Angular Forms?

Angular Forms are a vital part of building interactive web applications. They allow users to input and submit data, providing a way for developers to capture user information. Angular provides two approaches for creating forms: Template-driven Forms and Reactive Forms.

Why are Angular Forms important?

Angular Forms play a crucial role in capturing user input and validating it before submitting it to the server. They enable developers to create dynamic and interactive user interfaces, enhancing the overall user experience. Whether it's a simple contact form or a complex data entry form, Angular Forms provide a structured and efficient way to handle user input.

Overview of Angular Forms

Angular Forms can be created using either the Template-driven approach or the Reactive approach. Template-driven Forms rely on directives in the template to create and manipulate form controls. Reactive Forms, on the other hand, use a more programmatic approach, creating form controls and handling form events in the component class.

Template-driven Forms

Template-driven Forms are created using directives in the template. They are ideal for simpler forms with less complex validation requirements. Let's dive into creating a Template-driven Form.

Creating a Template-driven Form

To create a Template-driven Form, we need to add the ngForm directive to the form element and use the ngModel directive to bind form controls to properties in our component.

<form #myForm="ngForm">
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" [(ngModel)]="user.name" required>
  
  <button type="submit">Submit</button>
</form>

In the above example, we bind the name input field to the user.name property using the ngModel directive. The required attribute ensures that the field is not empty. The ngForm directive creates a form instance that we can reference using the #myForm template reference variable.

Form Controls

Form controls represent the input fields in our form. We can access these controls using the ngModel directive and interact with their properties and methods in our component. For example, we can check if a control is valid or dirty, retrieve its value, or set its value programmatically.

import { NgForm } from '@angular/forms';

@Component({...})
export class MyFormComponent {
  user = { name: '' };

  onSubmit(form: NgForm) {
    if (form.valid) {
      console.log(this.user.name);
    }
  }
}

In the above code snippet, we import the NgForm class from the @angular/forms package. We bind the form to the user object and define an onSubmit method that is called when the form is submitted. We can access the form controls using the form parameter and check if the form is valid before performing any actions.

Validation

Validation is an essential aspect of forms. Angular provides built-in validators and allows us to create custom validators to meet our specific validation requirements. We can apply validation rules to form controls using directives or programmatically in our component.

<input type="text" id="name" name="name" [(ngModel)]="user.name" required minlength="3">
<div *ngIf="myForm.controls.name.invalid && myForm.controls.name.touched">
  <div *ngIf="myForm.controls.name.errors.required">Name is required.</div>
  <div *ngIf="myForm.controls.name.errors.minlength">Name must be at least 3 characters long.</div>
</div>

In the above example, we apply the required and minlength validators to the name input field. We use the invalid and touched properties of the name control to display error messages conditionally.

Handling Form Submission

To handle form submission, we can bind the submit event of the form to a method in our component. Inside the method, we can perform any necessary actions, such as sending the form data to the server or resetting the form.

<form (submit)="onSubmit(myForm)">
  <!-- Form controls here -->
</form>

In the above code snippet, we bind the submit event of the form to the onSubmit method. We pass the myForm form instance to the method, allowing us to access the form controls and perform any necessary actions.

Reactive Forms

Reactive Forms provide a more programmatic approach to form creation and manipulation. They are suitable for complex forms with dynamic validation requirements. Let's explore how to create a Reactive Form.

Creating a Reactive Form

To create a Reactive Form, we need to import the necessary classes from the @angular/forms package and create an instance of the FormGroup class in our component.

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

@Component({...})
export class MyFormComponent {
  myForm: FormGroup;

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.myForm = this.formBuilder.group({
      name: ['', Validators.required]
    });
  }

  onSubmit() {
    if (this.myForm.valid) {
      console.log(this.myForm.value.name);
    }
  }
}

In the above code snippet, we import the FormBuilder, FormGroup, and Validators classes from the @angular/forms package. In the ngOnInit lifecycle hook, we create an instance of FormGroup using the formBuilder.group() method. We define the name control with an initial value of '' and apply the required validator.

Form Controls

Similar to Template-driven Forms, Reactive Forms also use form controls to represent input fields. We can access and manipulate these controls using the FormGroup instance in our component.

<input type="text" formControlName="name">

In the above example, we use the formControlName directive to bind the name input field to the corresponding control in the FormGroup. The value of the input field is automatically synchronized with the control's value.

Validation

Reactive Forms provide various built-in validators that we can apply to form controls using the Validators class. We can also create custom validators to meet our specific validation requirements.

this.myForm = this.formBuilder.group({
  name: ['', [Validators.required, Validators.minLength(3)]]
});

In the above code snippet, we apply the required and minLength validators to the name control. We pass an array of validators as the second argument to the formBuilder.group() method.

Handling Form Submission

To handle form submission in Reactive Forms, we can call a method in our component directly when the form is submitted.

<form [formGroup]="myForm" (submit)="onSubmit()">
  <!-- Form controls here -->
</form>

In the above code snippet, we bind the formGroup property of the form to the myForm instance in our component. We call the onSubmit method directly when the form is submitted.

Form Validation

Angular provides a range of options for form validation, including built-in validators, custom validators, and cross-field validation.

Built-in Validators

Angular offers a set of built-in validators that we can use to validate form controls. These validators include required, minLength, maxLength, pattern, and more. We can apply these validators to form controls using the Validators class.

this.myForm = this.formBuilder.group({
  name: ['', Validators.required],
  email: ['', [Validators.required, Validators.email]],
  password: ['', [Validators.required, Validators.minLength(8)]]
});

In the above example, we apply the required validator to the name control, the required and email validators to the email control, and the required and minLength validators to the password control.

Custom Validators

Angular allows us to create custom validators to meet our specific validation requirements. We can create a custom validator function and apply it to a form control using the Validators class.

function forbiddenNameValidator(control: AbstractControl): ValidationErrors | null {
  const forbiddenNames = ['admin', 'superuser'];

  if (forbiddenNames.includes(control.value)) {
    return { forbiddenName: true };
  }

  return null;
}

this.myForm = this.formBuilder.group({
  name: ['', [Validators.required, forbiddenNameValidator]]
});

In the above code snippet, we define a custom validator function forbiddenNameValidator that checks if the value of the control is in the forbiddenNames array. If the value is forbidden, we return a validation error object. Otherwise, we return null. We apply this custom validator to the name control.

Cross-field Validation

Cross-field validation is the process of validating multiple form controls together. Angular allows us to implement cross-field validation by creating a custom validator function that takes the entire form as an argument.

function passwordMatchValidator(form: FormGroup): ValidationErrors | null {
  const password = form.get('password');
  const confirmPassword = form.get('confirmPassword');

  if (password.value !== confirmPassword.value) {
    confirmPassword.setErrors({ passwordMatch: true });
  } else {
    confirmPassword.setErrors(null);
  }

  return null;
}

this.myForm = this.formBuilder.group({
  password: ['', Validators.required],
  confirmPassword: ['', Validators.required]
}, { validator: passwordMatchValidator });

In the above example, we define a custom validator function passwordMatchValidator that compares the values of the password and confirmPassword controls. If the values don't match, we set the passwordMatch error on the confirmPassword control. Otherwise, we remove the error. We apply this custom validator to the entire form using the validator property of the formBuilder.group() method.

Form Controls

Form controls provide a way to interact with user input in Angular Forms. Angular supports various types of form controls, including input controls, checkbox controls, radio button controls, select controls, and more.

Input Controls

Input controls allow users to enter text or select a value from a predefined set of options. We can create input controls using the input element or Angular's built-in form control components like input, textarea, and select.

<input type="text" formControlName="name">

In the above example, we create a text input control and bind it to the name control in the form using the formControlName directive.

Checkbox Controls

Checkbox controls allow users to select one or more options from a list of choices. We can create checkbox controls using the input element with the type="checkbox" attribute.

<input type="checkbox" formControlName="agree">

In the above example, we create a checkbox control and bind it to the agree control in the form using the formControlName directive.

Radio Button Controls

Radio button controls allow users to select a single option from a list of choices. We can create radio button controls using the input element with the type="radio" attribute.

<label>
  <input type="radio" [value]="true" formControlName="gender">
  Male
</label>
<label>
  <input type="radio" [value]="false" formControlName="gender">
  Female
</label>

In the above example, we create two radio button controls and bind them to the gender control in the form using the formControlName directive.

Select Controls

Select controls allow users to choose an option from a dropdown list. We can create select controls using the select element and the option element.

<select formControlName="country">
  <option value="">Select a country</option>
  <option value="USA">USA</option>
  <option value="Canada">Canada</option>
  <option value="UK">UK</option>
</select>

In the above example, we create a select control with multiple options and bind it to the country control in the form using the formControlName directive.

Form Arrays

Form Arrays provide a way to manage a dynamic list of form controls. They are useful when we need to handle a collection of related form controls that can be added or removed dynamically.

Creating Form Arrays

To create a Form Array, we need to use the FormArray class from the @angular/forms package. We can create an instance of FormArray and add it to our form group.

import { FormArray } from '@angular/forms';

this.myForm = this.formBuilder.group({
  names: this.formBuilder.array([])
});

get names(): FormArray {
  return this.myForm.get('names') as FormArray;
}

addName() {
  this.names.push(this.formBuilder.control(''));
}

removeName(index: number) {
  this.names.removeAt(index);
}

In the above code snippet, we import the FormArray class from the @angular/forms package. We create a Form Array called names and add it to our form group. We define getter methods to retrieve the names Form Array and add or remove controls from it.

Adding and Removing Form Controls

To add or remove form controls dynamically, we can use the methods provided by the FormArray class. We can use the push() method to add a control to the Form Array and the removeAt() method to remove a control at a specific index.

<div formArrayName="names">
  <div *ngFor="let name of names.controls; let i = index">
    <input type="text" [formControlName]="i">
    <button (click)="removeName(i)">Remove</button>
  </div>
</div>

<button (click)="addName()">Add Name</button>

In the above example, we use the formArrayName directive to bind the names Form Array to the div element. We use the ngFor directive to iterate over the controls in the Form Array and display an input field for each control. We bind the formControlName directive to the index of the control. We also provide a button to remove a control at a specific index and a button to add a new control to the Form Array.

Working with Form Array Values

To access the values of a Form Array, we can use the value property of the Form Array. It returns an array containing the values of all the controls in the Form Array.

onSubmit() {
  console.log(this.names.value);
}

In the above code snippet, we access the values of the names Form Array in the onSubmit method. We can perform any necessary actions with the array of values, such as sending them to the server.

Form Builder

The Form Builder is a utility class provided by Angular that simplifies the creation of forms. It provides a more concise and readable way to create form controls and form groups.

Using Form Builder

To use the Form Builder, we need to import the FormBuilder class from the @angular/forms package and inject it into our component constructor.

import { FormBuilder } from '@angular/forms';

constructor(private formBuilder: FormBuilder) {}

In the above code snippet, we import the FormBuilder class from the @angular/forms package and inject it into our component constructor.

Simplifying Form Creation

The Form Builder simplifies the creation of form controls and form groups by providing a chainable API. We can use methods like control() and group() to create form controls and form groups respectively.

this.myForm = this.formBuilder.group({
  name: this.formBuilder.control('', Validators.required),
  email: this.formBuilder.control('', Validators.email),
  password: this.formBuilder.control('', Validators.required)
});

In the above example, we use the Form Builder to create form controls with their initial values and validators. We pass these controls to the group() method to create a form group.

Conclusion

In this tutorial, we explored Angular Forms and covered both Template-driven Forms and Reactive Forms. We learned how to create forms, handle form submission, and implement form validation using built-in and custom validators. We also explored form controls, including input controls, checkbox controls, radio button controls, and select controls. Additionally, we discussed form arrays and the Form Builder utility class, which simplifies form creation. With this comprehensive guide, you should now have the knowledge and skills to effectively use Angular Forms in your applications.