FormGroup in Angular

The FormGroup is a collection of Form controls It Tracks the value and validity state of a group of Form control instances. The FormGroup is one of the building blocks of the angular forms. The other two are FormControl and FormArray. In this article, we will learn what is FormGroup is and learn some of its important properties & methods

What is FormGroup

Consider a simple HTML form

All of the above input fields are represented as the separate FormControl instance. We can use the FormControl instance to check the value & validity of each field. But if we wanted to check the validity of our entire form, we have to check the validity of each and every FormControl for validity. Imagine a form having a large no of fields. It is cumbersome to loop over large no of FormControl and check for validity.

The FormGroup solve this issue by providing a wrapper around a collection of FormControl’s It encapsulates all the information related to a group of form elements. It Tracks the value and validation status of each of these control. We can use it to check the validity of the elements. set its values & listen for change events, add and run validations on the group, etc

The FormGroup is just a class. We create a FormGroup to organize and manage the related elements. For Example form elements like address, city, state, pin code, etc can be grouped together as a single FormGroup. It makes it easier to manage them. A FormGroup aggregates the values of each child FormControl into one object, with each control name as the key. It 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.

Using FormGroup

Every Angular Form must have at least one top-level FormGroup. We use It to track the value & validity of the entire form.

The Angular has two approaches to building the Angular Forms. One is Template-driven and the other one is Reactive Forms.

To use the Angular forms, first, we need to import the FormsModule (for template-driven forms) & ReactiveFormsModule ( for Reactive Forms) from the @angular/forms in your route module.

Also, add it to the imports metadata

Reactive Forms

In Reactive forms, we create the form model in the component class. First, we need to import the FormGroup, FormControl, & Validators.

When instantiating a FormGroup, pass in a collection of child controls as the first argument. The key for each child registers the name for the control.

The following form model has two Form Groups. One is the top-level Form group, which we have named as reactiveForm. The other one is nested Form Group, which we have named it as address.

And in the Template, we use formGroup, formControlName and formGroupName directive to bind the Form to the template.

Template Driven forms

In Template-driven forms. the model is built in the template first. The top-level form is bound to ngForm directive, which we have named as templateForm. We add ngModel directive to each form element to create Form Controls. The name attribute will become the name of the Form Control. The ngModelGroup directive is used to create the nested Form Group.

We can get the reference to the top-level form group in component class using the ViewChild as shown below.

Setting Value

We use setValue or patchValue method of the FormGroup to set a new value for the entire FormGroup.

SetValue

Sets the value of the FormGroup. It accepts an object that matches the structure of the group, with control names as keys. The structure must match exactly, otherwise, it will result in an error.

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

You can also update the nested FormGroup separately,

patchValue

Patches the value of the FormGroup. It accepts an object with control names as keys and does its best to match the values to the correct controls in the group.

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

We can Both setValue & patchValue

  • onlySelf: When true, each change only affects this control and not its parent. The default is true.
  • emitEvent: When true or not supplied (the default), both the statusChanges and valueChanges observables emit events with the latest status and value when the control value is updated. When false, no events are emitted. The configuration options are passed to the updateValueAndValidity method.

Finding the Value

value

The value returns the object with a key-value pair for each member of the Form Group. It is Readonly. To Set Value either setValue or patchValue

value: any

valueChanges

valueChanges: Observable<any>

The angular emits the valueChanges event whenever the value of any of the controls in the Form Group changes. The value may change when user updates the element in the UI or programmatically through the setValue/patchValue method. We can subscribe to it as shown below

In the example below, the first valuesChanges are fired, when any of the control is changed. While the second valuesChanges event is raised only when the controls under the address form group are changed

Adding Controls Dynamically to Form Group

We usually add controls, while initializing the FormGroup.

The Forms API also allows add controls dynamically

addControl()

Adds a control to the FormGroup and also updates validity & validation status. If the control already exists, then ignores it

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

registerControl() 

Adds control to this FormGroup but does not update the validity & validation status. If the control already exists, then ignores it

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

removeControl() 

This method will remove the control with the provided name from the FormGroup.

removeControl(name: string): void

setControl()

Replaces the control with the provided name with the new control.

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

contains()

Check whether the control with the provided name exists or not..

contains(controlName: string): boolean

Control Status

The FormGroup tracks the validation status of all the FormControls, which is part of the FormGroup. That also includes the status of nested FormGroup or FormArray. If any of the control becomes invalid, then the entire FormGroup becomes invalid.

The following is the list of status-related properties

status

status: string

The Angular runs validation checks, whenever the value of a form control changes. Based on the result of the validation, the FormGroup can have four possible states.

VALID: All the controls of the FormGroup has passed all validation checks.
INVALID: At least one of the control has failed at least one validation check.
PENDING: This Group is in the midst of conducting a validation check.
DISABLED: This FormGroup is exempt from validation checks

valid

valid: boolean

A FormGroup is valid when it has passed all the validation checks and the FormGroup is not disabled

invalid

invalid: boolean

A FormGroup is invalid when one of its controls has failed a validation check or the entire FormGroup is disabled.

pending

pending: boolean

A FormGroup is pending when it is in the midst of conducting a validation check.

disabled

disabled: boolean

A FormGroup is disabled when all of its controls are disabled.

enabled

enabled: boolean

A FormGroup is enabled as long one of its control is enabled.

pristine

pristine: boolean

A FormGroup is pristine if the user has not yet changed the value in the UI in any of the controls

dirty

dirty: boolean

A FormGroup is dirty if the user has changed the value in the UI in any one of the control.

touched

touched: boolean

True if the FomGroup is marked as touched. A FormGroup is marked as touched once the user has triggered a blur event on any one of the controls

untouched

untouched: boolean

True if the FormGroup has not been marked as touched. A FormGroup is untouched if the user has not yet triggered a blur event on any of its child controls

Changing the Status

We can also change the status of the FormGroup by using the following method.

markAsTouched

The FormGroup is marked as touched if anyone of its control is marked as touched. The control is marked as touched once the user has triggered a blur event on it.

markAsTouched(opts: { onlySelf?: boolean; } = {}): void

  • onlySelf if true then only this control is marked. If false it will also mark all its direct ancestors also as touched. The default is false.

In the following example, the City is marked as touched. It will also mark both the address & reactiveFormGroup as touched.

By Passing the onlySelf:true argument, you can ensure that only the city is marked as touched, while address & reactiveForm are not affected.

The following code marks the address FormGroup as touched. while the child controls are not marked as touched. The parent FormGroup is marked as touched.

While onlySelf:true marks only the address group as touched, leaving the top-level FormGroup

markAllAsTouched

Marks the control and all its descendant controls as touched.

markAllAsTouched(): void

The following example marks the address and all its controls i.e city, state & address as touched. The parent FormGroup stays as it is.

markAsUntouched

Marks the control as untouched.

markAsUntouched(opts: { onlySelf?: boolean; } = {}): void

  • onlySelf if true only this control is marked as untouched. When false or not supplied, mark all direct ancestors as untouched. The default is false.

The following code will mark the city as untouched. It will recalculate the untouched & touched status of the parent Group. If all the other controls are untouched then the parent FormGroup address is also marked as untouched.

By using the onlySelf:true you can ensure that only the city is marked as untouched, leaving the parent FormGroup as it is.

Similarly, you can mark the entire FormGroup as untouched. While this does not have any effect on the child controls, but it does recalculate the untouched status of the parent FormGroup. You can use the onlySelf:true ensure that it does not happen.

markAsDirty

The FormGroup becomes dirty when any one of its control is marked as dirty. A control becomes dirty when the control’s value is changed through the UI. We can use the markAsDirty method to manipulate the dirty status.

markAsDirty(opts: { onlySelf?: boolean; } = {}): void

  • onlySelf if true, only this control is marked as dirty else all the direct ancestors are marked as dirty. The default is false.

The Following code marks the entire form as dirty. It does not change the status of any of the child controls.

The following code marks the City as dirty. It will also change the Dirty status of Parent FormGroup.

You can use the onlySelf:false to ensure that the parent FormGroup is not affected by our change.

You can also make the entire FormGroup as dirty. It does not affect the child controls, but parent FormGroup is also marked as dirty. Unless you pass the {onlySelf:true} argument

markAsPristine

The FormGroup becomes pristine when none of its controls values are changed via UI. The pristine is the opposite of dirty. We can use the markAsPrisitine method to manipulate the pristine status.

markAsPristine(opts: { onlySelf?: boolean; } = {}): void

  • onlySelf if true, only this control is marked as pristine else all the direct ancestors are marked as pristine. The default is false.

The following code marks the Form as Pristine. It will also mark all the child controls as Pristine

The following code marks the city as Pristine. It will also calculate the Pristine status of the Parent FormGroup. If all the other controls are pristine then the parent FormGroup becomes pristine.

You can make use of the onlySelf:true to ensure that the pristine status of the parent group is not calculated.

markAsPending

Marks the control as pending. We usually use this when we running our validation checks. Pending means the status of the control cannot be determined at this time.

markAsPending(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

  • onlySelf: When true, mark only this control. When false or not supplied, mark all direct ancestors. The default is false.
  • emitEvent: When true or not supplied (the default), the statusChanges observable emits an event with the latest status the control is marked pending. When false, no events are emitted.

The following code marks the entire form as Pending. It does not change the status of child Controls.

The following will mark the address FormGroup as Pending. It will also mark the Parent FormGroup as Pending also, which you can control using the onlySelf:true argument

This method also triggers the statusChange Event. You can make use of emitEvent:false argument, which will stop the statusChange event being triggered.

disable

Disables the control. This means the control is exempt from validation checks and excluded from the aggregate value of any parent. Its status is DISABLED.

disable(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

  • onlySelf: When true, mark only this control. When false or not supplied, mark all direct ancestors. Default is false..
  • emitEvent: When true or not supplied (the default), both the statusChanges and valueChanges observables emit events with the latest status and value when the control is disabled. When false, no events are emitted.

The following code disables all the controls in the FormGroup.

If you disable all the controls individually, then the FormGroup is automatically disabled.

enable

Enables control. This means the control is included in validation checks and the aggregate value of its parent. Its status recalculates based on its value and its validators.

enable(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

  • onlySelf: When true, mark only this control. When false or not supplied, mark all direct ancestors. The default is false.
  • emitEvent: When true or not supplied (the default), both the statusChanges and valueChanges observables emit events with the latest status and value when the control is enabled. When false, no events are emitted.

The following command enables all the controls in the Group. Even the controls previously disabled are also enabled.

Enables only address FormGroup.

Enable a Single Control

Status Change Event

statusChanges

statusChanges: Observable<any>

The statusChanges event is fired whenever the status of the form is calculated. We can subscribe to this event as shown below. We can subscribe it at the FormControl level or at the FormGroup level.

Note that this event is fired whenever the status is calculated.

In the example below, the first statusChanges is emitted, when the status of the top-level FormGroup is calculated. The second statusChange event is emitted, when the address FormGroup status is calculated.

Validation

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

updateValueAndValidity()

The updateValueAndValidity forces the form to perform validation. When applied to the FormGroup, it will calculate the validity of all the child controls, including nested form groups & form arrays This is useful when you add/remove validators dynamically using setValidators, RemoveValidators etc

updateValueAndValidity(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void

  • onlySelf: When true, only update this control. When false or not supplied, update all direct ancestors. Default is false..
  • emitEvent: When true or not supplied (the default), both the statusChanges and valueChanges observables emit events with the latest status and value when the control is updated. When false, no events are emitted.

setValidators() / setAsyncValidators()

Programmatically adds the sync or async validators. This method will remove all the previously added sync or async validators.

setValidators(newValidator: ValidatorFn | ValidatorFn[]): void
setAsyncValidators(newValidator: AsyncValidatorFn | AsyncValidatorFn[]): void

clearValidators() / clearAsyncValidators()

clearValidators(): void
clearAsyncValidators(): void

clearValidators & clearAsyncValidators clears all validators.

errors()

errors: ValidationErrors | null

An object containing any errors generated by failing validation, or null if there are no errors.

setErrors()

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

getError()

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

Reports error data for the control with the given path.

hasError

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

Reports whether the control with the given path has the error specified.

Reset

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

Resets the control. We can also pass the default value.

Summary

In this tutorial, we learned what is FormControl is and looked at the various methods & properties that are available.

Complete List of Angular Forms Tutorials

  1. Angular Forms Tutorial: Fundamental & Concepts
  2. Template Driven Forms in Angular
  3. Set Value in Template Driven forms in Angular
  4. Reactive Forms in Angular
  5. FormBuilder in Reactive Forms
  6. SetValue & PatchValue in Angular
  7. StatusChanges in Angular Forms
  8. ValueChanges in Angular Forms
  9. FormControl
  10. FormGroup
  11. FormArray Example
  12. Build Dynamic or Nested Forms using FormArray
  13. Validations in Reactive Forms in Angular
  14. Custom Validator in Reactive Forms
  15. Passing Parameter to Custom Validator in Reactive Forms
  16. Inject Service into Custom Validator
  17. Validation in Template Driven Forms
  18. Custom Validator in Template Driven Forms

2 thoughts on “FormGroup in Angular”

  1. Crystal clear detailed explanation.
    Landed in one of the page while searching for Angular reactive forms.
    Am going through the entire Angular tutorial now.
    Great work.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top