Thursday, April 25, 2024

FormGroup in Angular With Examples

Using FormGroup in Angular forms you can group FormControl instances and track the value and validity state of those instances as a group rather than for each individual FormControl instance. In this post we'll see example of using FormGroup with Angular template-driven form as well as with Reactive form.


Angular FormGroup class

FormGroup is one of the three fundamental building blocks used to define forms in Angular, along with FormControl and FormArray.

Using FormGroup class object you can encapsulate information about a group of FormControl instances that are part of FormGroup.

FormGroup calculates its status by reducing the status values of its children. For example, if one of the controls in a group is invalid, the entire group becomes invalid. That becomes convenient in a large form having many fields, rather than iterating over individual FormControls and checking each FormControl’s validity you can check the status of the FormGroup.

FormGroup also helps in structuring a large form in different groups of similar kind of FormControls. For example if you are creating a registration form you can put user data related form controls in one group, address related form controls in another group and so on.

Angular FormGroup template-driven form example

We’ll create a template driven form with two input fields for name and email, a date picker for picking date and a drop down to select one of the option apart from that there will be fields for address (House No., City, State and Pin code) that will be grouped in a FormGroup.

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;
  membershipDate: Date;
  membershipType: string;
  address: Address;
  constructor(name: string, mail: string, membershipDate: Date, membershipType: string, address: Address) {
    this.name = name;
    this.mail = mail;
    this.membershipDate  = membershipDate;
    this.membershipType = membershipType;
    this.address = address;
  }
}

Template (app.component.html)

<div class="container">
  <h1>Membership Form</h1>
  <form (ngSubmit)="onSubmit()" #membershipForm="ngForm">
    <div class="row mb-3">
      <div class="col-sm-12 col-md-6">
        <label class="form-label" for="name">Name</label>
        <input type="text" class="form-control" 
          id="name"
          ngModel name="name">                      
      </div>
      <div class="col-sm-12 col-md-6">
        <label class="form-label" for="email">Email</label>
        <input type="email" class="form-control" 
          id="email"
          ngModel name="email">                        
      </div>
    </div>
    <div class="row mb-3">
      <div class="col-sm-12 col-md-6">
        <label class="form-label" for="mdate">Membership Date</label>
        <input type="date" class="form-control" id="mdate"
            [ngModel]="currentDate | date:'yyyy-MM-dd'"  name="mdate">                        
      </div>
      <div class="col-sm-12 col-md-6">
        <label class="form-label" for="type">Membership Type</label>
        <select class="form-control" id="type"                    
            ngModel name="type">
            <option *ngFor="let mtype of membershiptypes" [value]="mtype">{{mtype}}</option>
        </select>
      </div>
    </div>
    <div ngModelGroup="addressgroup">
      <div class="row mb-3">
        <div class="col-sm-12 col-md-6">
          <label class="form-label" for="houseNo">House No.</label>
          <input type="text" class="form-control" 
            id="houseNo"
            ngModel name="houseNo">
        </div>
        <div class="col-sm-12 col-md-6">
          <label class="form-label" for="city">City</label>
          <input type="text" class="form-control" 
            id="city"
            ngModel name="city">
        </div>
      </div>
      <div class="row mb-3">
        <div class="col-sm-12 col-md-6">
          <label class="form-label" for="state">State</label>
          <input type="text" class="form-control" 
            id="state"
            ngModel name="state">
        </div>
        <div class="col-sm-12 col-md-6">
          <label class="form-label" for="pin">Pin Code</label>
          <input type="text" class="form-control" 
            id="pin"
            ngModel name="pin">
        </div>
      </div>
    </div>
    <button type="submit" class="btn btn-success">Submit</button>
  </form> 

  <hr>
  <div *ngIf="submitted">
    <div class="row mb-s">
      <div class="col-sm-10">
        <p>Name: {{member.name}}</p>
        <p>email: {{member.mail}}</p>
        <p>Membership Date: {{member.membershipDate | date:'dd/MM/yyyy'}}</p>
        <p>Membership Type: {{member.membershipType}}</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>

Important points to note here are-

  1. For the whole form a local reference “membershipForm” is created which is bound to the ngForm directive.
  2. ngModel is added with every element. This tells Angular that this element is a form control.
  3. name="xxxx" specifies the name of form control. Using the specified name this form control is registered in the form.
  4. The ngModelGroup directive is used to create a FormGroup which is a nested form group with in a form for grouping all the Address related form controls. Name given to the form group is "addressgroup".
    <div ngModelGroup="addressgroup">
    
  5. When the form is submitted it shows the entered values below the form fields. For that there is a ngIf directive to check if the form is submitted or not.
  6. In the data model address reference is stored with in the Member class so it is accessed the same way- member.address.FIELD_NAME

Component class (app.component.ts)

import { Component, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
import { Address } from './address.model';
import { Member } from './member.model';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  @ViewChild('membershipForm') memberForm! : NgForm;
  membershiptypes = ['Silver', 'Gold', 'Platinum'];
  currentDate = new Date();
  address = new Address('', '', '', '');
  member = new Member('', '', new Date(), '', this.address);
  submitted = false;

  onSubmit(){
    this.submitted = true;
    this.member.name = this.memberForm.value.name;
    this.member.mail = this.memberForm.value.email;
    this.member.membershipDate = this.memberForm.value.mdate;
    this.member.membershipType = this.memberForm.value.type;
    this.member.address.houseNo = this.memberForm.value.addressgroup.houseNo;
    this.member.address.city = this.memberForm.value.addressgroup.city;
    this.member.address.state = this.memberForm.value.addressgroup.state;
    this.member.address.pinCode = this.memberForm.value.addressgroup.pin;
  }
}

Important points to note here are-

  1. @ViewChild decorator is used here to access the local reference. Since local reference membershipForm holds a reference to the JS object for the form so this way you can get access to the form object in your TS code.
  2. currentDate field stores the current date, this is the field bound to the membership date in the template.
  3. You create an object of the Address class and another object of the Member class. Note that in the constructor of the Member class address is also passed. In these objects you will assign the field values when the form is submitted (onSubmit() is called).
  4. Notice how the nested FormGroup is accessed using the top level form reference- this.memberForm.value.addressgroup.houseNo
  5. submitted field is initialized as false initially and changed to true when onSubmit() method is called.

Importing FormsModule

Don’t forget to Add FormsModule in the imports section of the AppModule.

 imports: [
    BrowserModule,
    FormsModule
 ],

With that form is ready which you can access after compilation.

Angular FormGroup Reactive form example

In Angular Reactive form coding for FormGroup would move to the Component class.

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html', 
  providers: [DatePipe]
})
export class AppComponent implements OnInit {
  membershiptypes = ['Silver', 'Gold', 'Platinum'];
  currentDate = new Date();
  address = new Address('', '', '', '');
  member = new Member('', '', new Date(), '', this.address);
  submitted = false;
  membershipForm! : FormGroup;
  constructor(private datePipe: DatePipe){ }
  ngOnInit() {
    this.membershipForm = new FormGroup({
      memberName: new FormControl(null),
      email: new FormControl(null),
      mdate: new FormControl(this.datePipe.transform(this.currentDate, 'yyyy-MM-dd')),
      membershipType: new FormControl('Silver'), 
      address: new FormGroup({
        houseNo: new FormControl(null),
        city: new FormControl(null),
        state: new FormControl(null),
        pinCode: new FormControl(null)
      })
    });
  }
  onSubmit(){
    this.submitted = true;
    this.member.name = this.membershipForm.value.memberName;
    this.member.mail = this.membershipForm.value.email;
    this.member.membershipDate = this.membershipForm.value.mdate;
    this.member.membershipType = this.membershipForm.value.membershipType; 
    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 FormControl and FormGroup classes.
  2. A reference to FormGroup is created named membershipForm.
  3. Using that reference a new FormGroup object is created and an array of form controls are added to that FormGroup object.
  4. With in that FormGroup a nested FormGroup for address is created and the FormControls related to address are grouped with in this FormGroup.

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">    
        <label class="form-label" for="name">Name</label>
        <input type="text" class="form-control" id="name"
              formControlName="memberName">                        
      </div>
      <div class="col-sm-12 col-md-6">
        <label class="form-label"  for="email">Email</label>
        <input type="email" class="form-control" id="email"
          formControlName="email">                        
      </div>
    </div>
    <div class="row mb-3">
      <div class="col-sm-12 col-md-6">
        <label class="form-label"  for="mdate">Membership Date</label>
        <input type="date" class="form-control" id="mdate"
        formControlName="mdate">                        
      </div>
      <div class="col-sm-12 col-md-6">
        <label class="form-label"  for="type">Membership Type</label>
        <select class="form-control" id="type"                    
          formControlName="membershipType">
          <option *ngFor="let mtype of membershiptypes" [value]="mtype">{{mtype}}</option>
        </select>
      </div>
    </div>
    <div formGroupName="address">
      <div class="row mb-3">
        <div class="col-sm-12 col-md-6">
          <label class="form-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 class="form-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 class="form-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 class="form-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 mb-3">
      <div class="col-sm-12 col-md-6">
        <p>Name: {{member.name}}</p>
        <p>email: {{member.mail}}</p>
        <p>Membership Date: {{member.membershipDate | date:'dd/MM/yyyy'}}</p>
        <p>Membership Type: {{member.membershipType}}</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> 

Important points to note here are-

  1. You need to tell the template from where it has to get all the FormControls for the form that is done by binding the FormGroup reference.
    <form [formGroup]="membershipForm" (ngSubmit)="onSubmit()">
    
  2. For syncing up the form controls you will have to bind each form field with the form control created in the component. That is done by assigning formControlName to the name given in the component.
    <input type="text" class="form-control" id="name"
                     formControlName="memberName">  
    
  3. To indicate the formcontrols that are grouped with in the nested form group “address” formGroupName directive is used.
    <div formGroupName="address">
    

Importing ReactiveFormsModule

Don’t forget to Add ReactiveFormsModule in the imports section of the AppModule.

 imports: [
    BrowserModule,
    ReactiveFormsModule
 ],

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

>>>Return to Angular Tutorial Page


Related Topics

  1. Forms in Angular
  2. Angular Template-Driven Form Validation Example
  3. Angular Reactive Form Validation Example
  4. Nested Route (Child Route) in Angular
  5. Angular Custom Two-Way Data Binding

You may also like-

  1. How to Install Node.js and NPM in Windows
  2. How to Setup Angular
  3. Angular Event Binding With Examples
  4. Angular ngStyle Directive With Examples
  5. Lock Striping in Java Concurrency
  6. throw Statement in Java Exception Handling
  7. How to Run a Shell Script From Java Program
  8. ServiceLocatorFactoryBean in Spring

No comments:

Post a Comment