Wednesday, April 17, 2024

FormBuilder in Angular Example

In this tutorial we’ll see how to use FormBuilder in Angular to create a form. FormBuilder is a helper class that reduces the boilerplate code while building a form. It shortens the code to create instances of a FormControl, FormGroup, or FormArray.

Steps needed to use FormBuilder

  1. Import the FormBuilder class.
    import { FormBuilder } from '@angular/forms';
    
  2. Inject the FormBuilder service.
    constructor(private fb: FormBuilder) { }
    
  3. Generate the form contents. The FormBuilder has three factory methods: control(), group(), and array() used to generate instances of FormControl, FormGroup, and FormArray in your component classes.

FormBuilder Angular example

For the example we’ll create a Reactive form with fields name and email and a nested FromGroup that groups the controls for address (House No., City, State and Pin code).

Data Model

We’ll have separate Model classes also as Address and Member. In the Member class there will be a Address class reference.

address.model.ts

export class Address {
  houseNo: string;
  city: string;
  state: string;
  pinCode: string;
  constructor(houseNo: string, city: string, state: string, pinCode: string) {
    this.houseNo = houseNo;
    this.city = city;
    this.state  = state;
    this.pinCode = pinCode;
  }
}

member.model.ts

import { Address } from './address.model';

export class Member {
  name: string;
  mail: string;
  address: Address;
  constructor(name: string, mail: string, address: Address) {
    this.name = name;
    this.mail = mail;
    this.address = address;
  }
}

Component Class (app.component.ts)

import { Component, OnInit} from '@angular/core';
import { FormBuilder, FormGroup} from '@angular/forms';
import { Address } from './address.model';
import { Member } from './member.model';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html', 
})
export class AppComponent implements OnInit {
  constructor(private fb: FormBuilder) {}
  
  address = new Address('', '', '', '');
  member = new Member('', '', this.address);
  submitted = false;
  membershipForm: FormGroup;
  ngOnInit() {
    this.membershipForm = this.fb.group({
      memberName: [''],
      email: [''],
      address: this.fb.group({
        houseNo: [''],
        city: [''],
        state: [''],
        pinCode: ['']
      })
    });
  }

  onSubmit(){
    this.submitted = true;
    this.member.name = this.membershipForm.value.memberName;
    this.member.mail = this.membershipForm.value.email;
    this.member.address.houseNo = this.membershipForm.value.address.houseNo;
    this.member.address.city = this.membershipForm.value.address.city;
    this.member.address.state = this.membershipForm.value.address.state;
    this.member.address.pinCode = this.membershipForm.value.address.pinCode;
  }
}

Important points to not here are-

  1. group() method on the FormBuilder object is called to define the properties in the model.
  2. The value for each control name is an array containing the initial value as the first item in the array. Here initial value is passed as '' (empty control).
  3. You can add sync and async validators as the second and third items in the array.
  4. onSubmit() method is called when the form is submitted and the values entered in the form are used to create an object of Member class which also includes object of Address class.
  5. If you were not using FormBuilder, same form definition would have been written as given below.
    this.membershipForm = new FormGroup({
      memberName: new FormControl(null),
      email: new FormControl(null),
      address: new FormGroup({
        houseNo: new FormControl(null),
        city: new FormControl(null),
        state: new FormControl(null),
        pinCode: new FormControl(null)
      })
    });
    

Template (app.component.html)

<div class="container">
  <h1>Membership Form</h1>
  <form [formGroup]="membershipForm" (ngSubmit)="onSubmit()">
    <div class="row mb-3">
      <div class="col-sm-12 col-md-6">
        <div class="form-group">
          <label for="name">Name</label>
          <input type="text" class="form-control" id="name"
                formControlName="memberName">                        
        </div>
      </div>
      <div class="col-sm-12 col-md-6">
        <div class="form-group">
          <label for="email">email</label>
          <input type="email" class="form-control" id="email"
          formControlName="email">                        
        </div>
      </div>
    </div>
    <div formGroupName="address">
      <div class="row mb-3">
        <div class="col-sm-12 col-md-6">
          <label for="houseNo">House No.</label>
          <input type="text" class="form-control" 
          id="houseNo" formControlName="houseNo">
        </div>
        <div class="col-sm-12 col-md-6">
          <label for="city">City</label>
          <input type="text" class="form-control" 
          id="city" formControlName="city">
        </div>
      </div>
      <div class="row mb-3">
        <div class="col-sm-12 col-md-6">
          <label for="state">State</label>
          <input type="text" class="form-control" 
          id="state" formControlName="state">
        </div>
        <div class="col-sm-12 col-md-6">
          <label for="pinCode">Pin Code</label>
          <input type="text" class="form-control" 
          id="pinCode" formControlName="pinCode">
        </div>
      </div>
    </div>
    <button type="submit" class="btn btn-success">Submit</button>
  </form> 
  <hr>
  <div *ngIf="submitted">
    <div class="row">
      <div class="col-sm-12 col-md-6">
        <p>Name: {{member.name}}</p>
        <p>email: {{member.mail}}</p>
        <p>House Number: {{member.address.houseNo}}</p>
        <p>City: {{member.address.city}}</p>
        <p>State: {{member.address.state}}</p>
        <p>Pin Code: {{member.address.pinCode}}</p>
      </div>
    </div>
  </div>  
</div>

Form Validation With FormBuilder

To add validation you can pass Validators as the second element in the array created for each control name.

Refer this tutorial Angular Reactive Form Validation Example to know more about Reactive form validation.

In the form with validation example we’ll create a form with green bars at the left side of the controls that are required. That bar changes to red if value is not entered for the required field or value is invalid and an error message is also displayed.

Initial form state-

FormBuilder in Angular

With invalid values-

FormBuilder validation example

CSS class (src/assets/forms.css)

You will need to add a CSS class to show a colored bar on the left side of the control.

.ng-invalid.ng-untouched:not(form):not(div)  {
  border-left: 5px solid #42A948; /* green */
}
  
.ng-invalid.ng-touched:not(form):not(div)  {
  border-left: 5px solid #a94442; /* red */
}

.ng-invalid.ng-untouched:not(form):not(div) means select all elements having both ng-invalid and ng-untouched set within its class attribute and it is not at form or div level. If you won’t add not(form) you will have a bar at the form level too encompassing all controls and not having at div level ensures that you don’t have a bar at the whole group level.

In the index.html file, update the <head> tag to include the new style sheet.

<link rel="stylesheet" href="assets/forms.css">

Component Class (app.component.ts)

import { Component, OnInit} from '@angular/core';
import { FormBuilder, FormGroup, Validators} from '@angular/forms';
import { Address } from './address.model';
import { Member } from './member.model';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html', 
})
export class AppComponent implements OnInit {
  constructor(private fb: FormBuilder) {}
  
  address = new Address('', '', '', '');
  member = new Member('', '', this.address);
  submitted = false;
  membershipForm : FormGroup;
  ngOnInit() {
    this.membershipForm = this.fb.group({
      memberName: ['', [Validators.required, Validators.minLength(5)]],
      email: ['', [Validators.required, Validators.email]],
      address: this.fb.group({
        houseNo: ['', Validators.required],
        city: [''],
        state: [''],
        pinCode: ['']
      })
    });
  }

  onSubmit(){
    this.submitted = true;
    this.member.name = this.membershipForm.value.memberName;
    this.member.mail = this.membershipForm.value.email;
    this.member.address.houseNo = this.membershipForm.value.address.houseNo;
    this.member.address.city = this.membershipForm.value.address.city;
    this.member.address.state = this.membershipForm.value.address.state;
    this.member.address.pinCode = this.membershipForm.value.address.pinCode;
  }
}

Important points to note here are-

  1. You need to import Validators.
  2. Reactive forms include a set of built-in validator functions for common use cases. These functions receive a control to validate against and return an error object or a null value based on the validation check.
  3. If multiple validators are passed you pass them as an array of validators as shown here.
    memberName: ['', [Validators.required, Validators.minLength(5)]]
    

Template (app.component.html)

<div class="container">
  <h1>Membership Form</h1>
  <form [formGroup]="membershipForm" (ngSubmit)="onSubmit()">
    <div class="row mb-3">
      <div class="col-sm-12 col-md-6">
        <div class="form-group">
          <label for="name">Name</label>
          <input type="text" class="form-control" id="name"
                formControlName="memberName"> 
            <div class="alert alert-danger" *ngIf="membershipForm.get('memberName')?.invalid 
             && membershipForm.get('memberName')?.touched">
              <div *ngIf="membershipForm.get('memberName')?.hasError('required')">
                Name is required.
              </div>
              <div *ngIf="membershipForm.get('memberName')?.hasError('minlength')">
                Name must be at least 5 characters long.
              </div>  
            </div>                       
        </div>
      </div>
      <div class="col-sm-12 col-md-6">
        <div class="form-group">
          <label for="email">email</label>
          <input type="email" class="form-control" id="email"
          formControlName="email">  
          <div class="alert alert-danger" *ngIf="membershipForm.get('email')?.invalid 
            && membershipForm.get('email')?.touched">
            Please enter a valid email
          </div>                      
        </div>
      </div>
    </div>
    <div formGroupName="address">
      <div class="row mb-3">
        <div class="col-sm-12 col-md-6">
          <label for="houseNo">House No.</label>
          <input type="text" class="form-control" 
          id="houseNo" formControlName="houseNo">
          <div class="alert alert-danger" *ngIf="membershipForm.get('address')?.get('houseNo')?.invalid && 
            (membershipForm.get('address')?.get('houseNo')?.dirty || membershipForm.get('address')?.get('houseNo')?.touched)">
            Please enter house number
          </div>
        </div>
        <div class="col-sm-12 col-md-6">
          <label for="city">City</label>
          <input type="text" class="form-control" 
          id="city" formControlName="city">
        </div>
      </div>
      <div class="row mb-3">
        <div class="col-sm-12 col-md-6">
          <label for="state">State</label>
          <input type="text" class="form-control" 
          id="state" formControlName="state">
        </div>
        <div class="col-sm-12 col-md-6">
          <label for="pinCode">Pin Code</label>
          <input type="text" class="form-control" 
          id="pinCode" formControlName="pinCode">
        </div>
      </div>
    </div>
    <button type="submit" [disabled]="membershipForm.invalid" class="btn btn-success">Submit</button>
  </form> 
  <hr>
  <div *ngIf="submitted">
    <div class="row">
      <div class="col-sm-12 col-md-6">
        <p>Name: {{member.name}}</p>
        <p>email: {{member.mail}}</p>
        <p>House Number: {{member.address.houseNo}}</p>
        <p>City: {{member.address.city}}</p>
        <p>State: {{member.address.state}}</p>
        <p>Pin Code: {{member.address.pinCode}}</p>
      </div>
    </div>
  </div>  
</div>
  • Error message that is shown beneath the form controls is done by adding a <div> with the form control where message is needed. For example if you want to show error message for name control.
    <div class="alert alert-danger" *ngIf="membershipForm.get('email')?.invalid 
                && membershipForm.get('email')?.touched">
      Please enter a valid email
    </div>          
    
    Here alert and alert-danger are Bootstrap classes. Message is displayed if the control has been visited and email is not entered which is taken care of by
    *ngIf="membershipForm.get('email')?.invalid 
                && membershipForm.get('email')?.touched 
    
  • For accessing form control you can use get() method and pass the control name.
  • Validation classes are added at the form level too apart from with each form control to determine whether the form as a whole is valid or not. Using that you can disable the submit button and enable it only when the whole form is valid. That’s what [disabled]="membershipForm.invalid" does in the submit button.

That's all for this topic FormBuilder in Angular Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Angular Tutorial Page


Related Topics

  1. Angular Template-Driven Form Validation Example
  2. Angular Form setValue() and patchValue()
  3. Radio Button in Angular Form Example
  4. Checkbox in Angular Form Example
  5. Custom Async Validator in Angular Reactive Form

You may also like-

  1. Angular - Call One Service From Another
  2. Highlight Currently Selected Menu Item Angular Routing Example
  3. Setting Wild Card Route in Angular
  4. Angular ngIf Directive With Examples
  5. Bounded Type Parameter in Java Generics
  6. New Date And Time API in Java 8
  7. Abstract Class in Java
  8. Compressing File in snappy Format in Hadoop - Java Program

No comments:

Post a Comment