FormGroup In Angular

FormGroup In Angular

In this article, We will learn what a FormGroup is and some of its most significant characteristics and methods. A FormGroup is a group of Form controls. It keeps track of the value and validity of a collection of Form control instances. The FormGroup is one of the angular shapes' building blocks. FormControl and FormArray are the other two.

What is FormGroup

Consider a simple HTML form.

<form>
  First Name : <input type="text" name="firstname" /> 
  Last Name  : <input type="text" name="lastname" /> 
  Email      : <input type="text" name="email" /> 
</form>

For each of these input fields, we build a FormControl. It keeps track of these parts' worth and validity. Each of the input fields shown above is represented by a single FormControl. If we wanted to make sure our form was legitimate, we had to check the validity of each and every FormControl. Consider a form with a huge number of fields. It's inconvenient to loop over a large number of FormControl and verify for validity.

The FormGroup solves this problem by wrapping a set of FormControl in a wrapper. It contains all of the information about a set of form elements. It keeps track of the value and status of each of these controls. It can be used to verify the elements' validity.

Using FormGroup

There are two methods for creating Angular Forms in Angular. The first is template-driven, whereas the second is Reactive Forms.

To use Angular forms, import the FormsModule (for template-driven forms) and ReactiveFormsModule (for Reactive Forms) from @angular/forms in your route module first.
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

Also, include it in the metadata for imports.
imports: [
  BrowserModule,
  AppRoutingModule,
  FormsModule,
  ReactiveFormsModule
],

Reactive Forms

In Reactive forms, the form model is created in the component class. We must first import the FormGroupFormControl, and Validators.
import { FormGroup, FormControl, Validators } from '@angular/forms'

Pass a collection of child controls as the first argument when creating a FormGroup. The name of the control is registered in the key for each child.

There are two Form Groups in the following form model. One is the reactiveForm group, which is the top-level Form group. The other is a nested Form Group that which we named address.
reactiveForm = new FormGroup({
  firstname: new FormControl('', [Validators.required]),
  lastname: new FormControl(''),
  email: new FormControl(''),
  address: new FormGroup({
    address: new FormControl(''),
    city: new FormControl(''),
    state: new FormControl(''),
  })
})

To link the Form to the template, we use the formGroup, formControlName, and formGroupName directives in the Template.
<form [formGroup]="reactiveForm" (ngSubmit)="onSubmit()" novalidate>
 
    <p>
      <label for="firstname">First Name </label>
      <input type="text" id="firstname" name="firstname" formControlName="firstname">
    </p>
 
    <p>
      <label for="lastname">Last Name </label>
      <input type="text" id="lastname" name="lastname" formControlName="lastname">
    </p>
 
    <p>
      <label for="email">Email </label>
      <input type="text" id="email" name="email" formControlName="email">
    </p>
 
    <div formGroupName="address">
 
      <p>
        <label for="address">Address</label>
        <input type="text" class="form-control" name="address" formControlName="address">
      </p>
 
      <p>
        <label for="city">City</label>
        <input type="text" class="form-control" name="city" formControlName="city">
      </p>
 
      <p>
        <label for="state">State</label>
        <input type="text" class="form-control" name="state" formControlName="state">
      </p>
 
    </div>
 
    <button>Submit</button>
 
  </form>

Template Driven forms

In forms that are template-driven. The model is created initially in the template. The templateForm directive is used to bind the top-level form to the ngForm directive. To make Form Controls, we apply the ngModel directive to each form element. The Form Control's name will be determined by the name attribute. The nested Form Group is created with the ngModelGroup directive.
<form #templateForm="ngForm" (ngSubmit)="onSubmit(templateForm)">
 
    <p>
      <label for="firstname">First Name </label>
      <input type="text" id="firstname" name="firstname" ngModel required>
    </p>
    <p>
      <label for="lastname">Last Name </label>
      <input type="text" id="lastname" name="lastname" ngModel>
    </p>
 
    <p>
      <label for="email">Email </label>
      <input type="text" id="email" name="email" ngModel>
    </p>
 
 
    <div ngModelGroup="address">
 
      <p>
        <label for="address">Address</label>
        <input type="text" class="form-control" name="address" ngModel>
      </p>
 
      <p>
        <label for="city">City</label>
        <input type="text" class="form-control" name="city" ngModel>
      </p>
 
      <p>
        <label for="state">State</label>
        <input type="text" class="form-control" name="state" ngModel>
      </p>
    </div>
 
    <p>
      <button type="submit">Submit</button>
    </p>
    <div>
    </div>
 
  </form>

Using the ViewChild, we can retrieve a reference to the top-level form group in the component class, as seen below.
import { NgForm, Validators } from '@angular/forms';
 
export class TemplateComponent implements OnInit {
   @ViewChild('templateForm', null) templateForm: NgForm;
 
   ....
}

Setting Value

To change the value of the entire FormGroup, we use the setValue or patchValue methods.

SetValue

The FormGroup's value is set. It accepts an object with control names as keys that fits the group's structure. The structure must be same; otherwise, an error will occur.

Syntax
setValue(value: { [key: string]: any; }, options: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

Example
setValue() {
 
  this.reactiveForm.setValue({
    firstname: "Sachin",
    lastname: "Tendulakr",
    email: "sachin@gmail.com",
    address: {
      address: "19-A, Perry Cross Road, Bandra (West)",
      city: "Mumbai",
      state: "Maharatsra",
    }
  })
}

You can also make individual changes to the nested FormGroup.
setAddress() {
  this.reactiveForm.get("address").setValue({
    address: "19-A, Perry Cross Road, Bandra (West)",
    city: "Mumbai",
    state: "Maharatsra",
  })
}

patchValue

Patches the FormGroup's value. It takes an object containing control names as keys and tries to match the values to the correct controls in the group as best it can.

Syantax
patchValue(value: { [key: string]: any; }, options: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

Example
patchValue() {
 
  this.reactiveForm.patchValue({
    email: "sachin@gmail.com",
    address: {
      state: "Maharatsra",
    }
  })
}

We can Both setValue & patchValue

onlySelf: When true, each modification has no effect on its parent and just impacts this control. True is the default value.

emitEvent: When the control value is updated, both the statusChanges and valueChanges observables broadcast events with the newest status and value, whether true or not supplied (the default). When this value is false, no events are generated. The updateValueAndValidity function receives the configuration options.

Finding the Value

The value returns an object containing a key-value pair for each Form Group member. It's in read-only mode. setValue or patchValue can be used to set a value.

value: any

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

valueChanges

valueChanges: Observable<any>

When the value of any of the controls in the Form Group changes, the component broadcasts the valueChanges event. The value may change when the user modifies the element in the UI or when the setValue/patchValue method is used programmatically. As indicated below, we can subscribe to it.

When any of the controls in the example below is changed, the first valuesChanges are fired. The second valuesChanges event, on the other hand, is only triggered when the controls in the address form group are updated.
ngOnInit() {
  this.reactiveForm.valueChanges.subscribe(x => {
    console.log(x);
  })
  this.reactiveForm.get("address").valueChanges.subscribe(x => {
    console.log(x);
  })
}

Adding Controls Dynamically to Form Group

When we initialize the FormGroup, we normally add controls.
reactiveForm = new FormGroup({
  firstname: new FormControl('', [Validators.required]),
 
}

The Forms API also allows you to dynamically add controls.

addControl()

Adds a control to the FormGroup and modifies the validation and validity status. If the control is already in place, it is ignored.

Syntax
addControl(name: string, control: AbstractControl): void

Example
addControl() {
  this.middleName = new FormControl('', [Validators.required]);
  this.reactiveForm.addControl("middleName",this.middleName);
}

registerControl() 

This FormGroup gets a new control, but the validity and validation status remain unchanged. If the control is already in place, it is ignored.

Syantax
registerControl(name: string, control: AbstractControl): AbstractControl

Example
registerControl() {
  this.middleName = new FormControl('', [Validators.required]);
  this.reactiveForm.addControl("middleName",this.middleName);
}

removeControl()

The control with the specified name will be removed from the FormGroup using this technique.

Syntax
removeControl(name: string): void

Example
remodeControl() {
  this.reactiveForm.removeControl("middleName");
}

setControl()

The new control takes the place of the control with the given name.

Syantax
setControl(name: string, control: AbstractControl): void

Example
setControl() {
  this.middleName = new FormControl('test', [Validators.required]);
  this.reactiveForm.setControl("middleName",this.middleName);
}

contains()

Check to see if the control with the given name exists.

Syantax
contains(controlName: string): boolean

Example
containsControl() {
  console.log(this.reactiveForm.contains("middleName"));
}

Control Status

The FormGroup keeps track of the validation state of all the FormControls it contains. The state of nested FormGroup or FormArray is also included. The entire FormGroup becomes invalid if any of the controls become invalid.

The following is a list of properties connected to status.

status

Syantax
status: string


When the value of a form control changes, Angular performs validation checks. The FormGroup might have four different states depending on the validation outcome.

VALID: All of the FormGroup's controls have passed all validation checks.
INVALID: At least one validation check has failed for at least one of the controls.
PENDING: This group is currently performing a validation check.
DISABLED: Validation checks are not performed on this FormGroup.
//reactive forms
this.reactiveForm.status

valid

Syantax
valid: boolean

When a FormGroup passes all validation checks and is not disabled, it is considered legitimate.
this.reactiveForm.valid

invalid

Syantax
invalid: boolean

When one of the controls in a FormGroup fails a validity check or the entire FormGroup is disabled, the FormGroup becomes invalid.
this.reactiveForm.invalid

pending

Syantax
pending: boolean

When a FormGroup is in the middle of performing a validation check, it is marked as pending.
this.reactiveForm.pending 

disabled

Syantax
disabled: boolean

When all of the controls in a FormGroup are disabled, it is considered disabled.
this.reactiveForm.disabled

enabled

Syantax 
enable: boolean
A FormGroup is active if at least one of its controls is active.
this.reactiveForm.disabled

pristine

Syantax
pristine: boolean

If the user hasn't modified the value of any of the controls in the UI, the FormGroup is clean.
this.reactiveForm.pristine

dirty

Syantax
dirty: boolean

If the user has modified the value of any of the controls in the UI, the FormGroup is dirty.
this.reactiveForm.dirty

touched

Syntax
touched: boolean

If the FomGroup has been touched, this value is true. When a user triggers a blur event on any of the controls in a FormGroup, it is registered as touched.
this.reactiveForm.touched

untouched

Syantax 
untouched: boolean

If the FormGroup has not been tagged as touched, this value is true. If the user has not yet triggered a blur event on any of the FormGroup's child controls, it is considered undisturbed.
this.reactiveForm.untouched

Changing the Status

We may also use the following way to update the state of the FormGroup.

markAsTouched

If any of the FormGroup's controls are touched, the FormGroup is tagged as touched. Once the user has initiated a blur event on the control, it is registered as touched.
markAsTouched(opts: { onlySelf?: boolean; } = {}): void

onlySelf: Only this control is marked if true. If it is false, it will mark all of its direct ancestors as touched as well. False is the default value.

The City is marked as touched in the example below. Both the address and the reactiveFormGroup will be marked as touched.
markCityAsTouched() {
  this.reactiveForm.get("address").get("city").markAsTouched();
}

You may verify that just the city is recognized as impacted by passing the onlySelf:true argument, while the address and reactiveForm are unaffected.
markCityAsTouched() {
  this.reactiveForm.get("address").get("city").markAsTouched({onlySelf:true});
}

The code below indicates that the address FormGroup has been touched. The child controls, on the other hand, are not indicated as touched. The parent FormGroup has been indicated as having been touched.
markAddressAsTouched() {
  this.reactiveForm.get("address").markAsTouched();
}

Only the address group is marked as touched by onlySelf:true, leaving the top-level FormGroup unaffected.
markAddressAsTouched() {
  this.reactiveForm.get("address").markAsTouched({onlySelf:true});
}

markAllAsTouched

The control, as well as all descendant controls, is marked as touched.
markAllAsTouched(): void

The address and all of its controls, such as city, state, and address, are marked as touched in the following example. The parent FormGroup remains unchanged.
markAllAddressTouched() {
  this.reactiveForm.get("address").markAllAsTouched();
}

markAsUntouched

The control is marked as undisturbed.
markAsUntouched(opts: { onlySelf?: boolean; } = {}): void

onlySelf: Only this control is marked as untouched if true. Mark all direct ancestors as unaffected if the information is inaccurate or not provided. False is the default value.

The city will be marked as unaffected by the following code. It will recalculate the parent Group's untouched and touched status. If all of the other controls are left alone, the parent FormGroup address is also left alone.
markCityAsUnTouched() {
  this.reactiveForm.get("address").get("city").markAsUntouched();
}

You may ensure that only the city is identified as undisturbed by using the onlySelf:true property, while the parent FormGroup remains unchanged.
markCityAsUnTouched() {
  this.reactiveForm.get("address").get("city").markAsUntouched({onlySelf:true});
}

You can even leave the entire FormGroup unchanged. While this has no effect on the child controls, it does cause the parent FormGroup's untouched status to be recalculated. OnlySelf:true can be used to assure that this does not happen.
markAddressAsUnTouched() {
  this.reactiveForm.get("address").markAsUntouched();
}

markAsDirty

When any of the FormGroup's controls is designated as dirty, the FormGroup becomes filthy. When the value of a control is updated via the user interface, it becomes dirty. To change the filthy status, we can use the markAsDirty method.
markAsDirty(opts: { onlySelf?: boolean; } = {}): void

onlySelf: If this is the case, only this control will be tagged as dirty; otherwise, all immediate ancestors will be marked as dirty. False is the default value.

The code below makes the entire form dirty. It has no effect on the state of the kid controls.
markFormAsDirty() {
  this.reactiveForm.markAsDirty();
}

The following code indicates that the city is filthy. It will also change the Parent FormGroup's Dirty status.
markCityAsDirty() {
  this.reactiveForm.get("address").get("city").markAsDirty();
}

OnlySelf:false can be used to verify that our update has no effect on the parent FormGroup.
markCityAsDirty() {
  this.reactiveForm.get("address").get("city").markAsDirty({onlySelf:false});
}

You can also make the FormGroup as dirty as a whole. It has no effect on the child controls, but it does mark the parent FormGroup as unclean. Unless the onlySelf:true parameter is specified.
markAddressAsDirty() {
  this.reactiveForm.get("address").markAsDirty({onlySelf:false});
}

markAsPristine

When none of the controls' values are modified via the user interface, the FormGroup becomes clean. Pristine is the polar opposite of filthy. To control the pristine condition, we can use the markAsPrisitine method.
markAsPristine(opts: { onlySelf?: boolean; } = {}): void

onlySelf: If this is the case, only this control will be designated as pristine; otherwise, all immediate ancestors will be tagged as pristine. False is the default value.

The Form is marked as Pristine with the following code. All of the child controls will be marked as Pristine as well.
markFormAsPristine() {
  this.reactiveForm.markAsPristine();
}

The city is designated as Pristine by the following code. It will also calculate the Parent FormGroup's Pristine status. If all of the other controls are perfect, the parent FormGroup will be perfect as well.
markCityAsPristine() {
  this.reactiveForm.get("address").get("city").markAsPristine({onlySelf:false});
}

You can use the onlySelf:true to ensure that the parent group's immaculate status is not calculated.

markAsPending

The control is marked as pending. When we perform our validation checks, we normally use this. The status of the control can't be established right now because it's pending.
markAsPending(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

onlySelf: If true, only this control will be marked. Mark all direct ancestors if they are false or not provided. False is the default value.

emitEvent: The statusChanges observable produces an event with the most recent status the control is tagged pending whether true or not specified (the default). When this value is false, no events are generated.

The code below sets the status of the entire form to Pending. The status of kid Controls is unaffected.
this.reactiveForm.markAsPending();

The address FormGroup will be marked as Pending if you do the following. The Parent FormGroup will also be marked as Pending, which you can control with the onlySelf:true parameter.
markAddressAsPendng() {
  this.reactiveForm.get("address").markAsPending();
}

The statusChange Event is likewise triggered by this way. The emitEvent:false option can be used to prevent the statusChange event from being fired.
markAddressAsPendng() {
  this.reactiveForm.get("address").markAsPending({emitEvent:false});
}

disable

The control is turned off. This indicates the control is not subject to validity tests and is not included in any parent's aggregate value. Its current state is DISABLED.
disable(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

onlySelf: If true, only this control will be marked. Mark all direct ancestors if they are false or not provided. The default value is false.

emitEvent: When the control is disabled, both the statusChanges and valueChanges observables emit events with the most recent status and value, whether true or not given (the default). When this value is false, no events are generated.

The code below turns off all of the controls in the FormGroup.
disableAll() {
  this.reactiveForm.disable();
}

If you disable all of the controls separately, the FormGroup will be disabled as well.
disableAll() {
  this.reactiveForm.get("firstname").disable();
  this.reactiveForm.get("lastname").disable();
  this.reactiveForm.get("email").disable();
  this.reactiveForm.get("address").disable();
}

enable

Allows for control. This indicates that the control is included in validation checks and the parent's aggregate value. Its value and validators are used to recalculate its status.
enable(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

onlySelf: If true, only this control will be marked. Mark all direct ancestors if they are false or not provided. False is the default value.

emitEvent: When the control is enabled, both the statusChanges and valueChanges observables broadcast events with the most recent status and value, whether true or not supplied (the default). When this value is false, no events are generated.

The following command enables all of the Group's controls. Even the controls that were previously disabled have been reactivated.
enableAll() {
   this.reactiveForm.enable();
}

Allows only address FormGroup.
enableAddress() {
  this.reactiveForm.get("address").enable();
}

Allow only single control.
enableFirstName() {
  this.reactiveForm.get("firstname").enable();
}

Status Change Event

statusChanges

statusChanges: Observable<any>

When the form's status is calculated, the statusChanges event is triggered. This event can be subscribed to as mentioned below. We can subscribe to it either at the FormControl or FormGroup level.

When the status of the top-level FormGroup is calculated, the first statusChanges is emitted, as shown below. When the address FormGroup status is calculated, the second statusChange event is emitted.
this.reactiveForm.statusChanges.subscribe(x => {
  console.log(x);
})
 
this.reactiveForm.get("address").statusChanges.subscribe(x => {
   console.log(x);
})

Validation

The validators can be added to the FormArray, FormGroup, or FormControl.

updateValueAndValidity()

The updateValueAndValidity method makes the form validate itself. It will calculate the validity of all child controls, including nested form groups and form arrays, when applied to the FormGroup. This is useful when using setValidators and RemoveValidators to dynamically add and remove validators.
updateValueAndValidity(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

onlySelf: Only update this control if true. Update all direct ancestors if false or not specified. The default value is false.

emitEvent: When the control is updated, both the statusChanges and valueChanges observables broadcast events with the newest status and value, whether true or not supplied (the default). When this value is false, no events are generated.
this.reactiveForm.updateValueAndValidity();
this.reactiveForm.get("address").updateValueAndValidity();

setValidators() / setAsyncValidators()

The sync or async validators are added programmatically. This function will remove any sync or async validators that were previously installed.
setValidators(newValidator: ValidatorFn | ValidatorFn[]): void
setAsyncValidators(newValidator: AsyncValidatorFn | AsyncValidatorFn[]): void

setValidator() {
  this.reactiveForm.get("address").setValidators([addressValidator]);
  this.reactiveForm.get("address").updateValueAndValidity();
}
 
 
export const addressValidator = (control: AbstractControl): {[key: string]: boolean} => {
  const city = control.get('city').value;
  const state = control.get('state').value;
  console.log(control.value);
  if (city=="" && state=="") {
    return { address:false };
  }
  return null;
};

clearValidators() / clearAsyncValidators()


clearValidators(): void
clearAsyncValidators(): void

All validators are cleared with clearValidators and clearAsyncValidators.
//reactive forms
clearValidation() {
   this.reactiveForm.get("address").clearValidators();
   this.reactiveForm.get("address").updateValueAndValidity();
}

errors()

errors: ValidationErrors | null
If validation fails, this object contains any errors that are generated, or null if no errors are generated.
getErrors() {
 
  const controlErrors: ValidationErrors = this.reactiveForm.errors;
  if (controlErrors) {
    Object.keys(controlErrors).forEach(keyError => {
      console.log("firtname "+ ' '+keyError);
    });
  }
}

setErrors()

setErrors(errors: ValidationErrors, opts: { emitEvent?: boolean; } = {}): void

setErrors() {
  this.reactiveForm.setErrors( {customerror:'custom error'});
}

getError()

getError(errorCode: string, path?: string | (string | number)[]): any

Error data for the control with the supplied URL is reported.
this.reactiveForm.getError("firstname")
 
this.reactiveForm.getError("address.pincode");
this.reactiveForm.getError(["address","pincode"]);

hasError

hasError(errorCode: string, path?: string | (string | number)[]): boolean

Returns true if the control with the supplied path contains the stated error.
this.reactiveForm.hasError("firstname")
 
//
this.reactiveForm.hasError("address.pincode");
this.reactiveForm.hasError(["address","pincode"]);

Reset

abstract reset(value?: any, options?: Object): void

The control is reset. The default value can also be given.
this.reactiveForm.get("firstname").reset('');
this.reactiveForm.get("firstname").reset('test');

Conclusion

In this article, We learned what is FromGroup and looked at the various methods and properties.

I hope this article helps you and you will like it.👍

If you have any doubt or confusion then free to ask in comment section.

Post a Comment

Previous Post Next Post