> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angular/angular/llms.txt
> Use this file to discover all available pages before exploring further.

# FormGroup

> API reference for Angular's FormGroup class, which tracks the value and validity state of a group of FormControl instances.

The `FormGroup` class tracks the value and validity state of a group of `FormControl` instances. It aggregates the values of each child control into one object, with each control name as the key. It calculates its status by reducing the status values of its children.

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

## Import

```ts theme={null}
import { FormGroup, FormControl } from '@angular/forms';
```

## Constructor

<ParamField path="controls" type="TControl" required>
  A collection of child controls. The key for each child is the name under which it is registered.
</ParamField>

<ParamField path="validatorOrOpts" type="ValidatorFn | ValidatorFn[] | AbstractControlOptions | null" optional>
  A synchronous validator function, an array of such functions, or an `AbstractControlOptions` object that contains validation functions and a validation trigger.
</ParamField>

<ParamField path="asyncValidator" type="AsyncValidatorFn | AsyncValidatorFn[] | null" optional>
  A single async validator or array of async validator functions.
</ParamField>

## Basic Usage

### Creating a FormGroup

```ts theme={null}
import { FormGroup, FormControl, Validators } from '@angular/forms';

const form = new FormGroup({
  firstName: new FormControl('Nancy', Validators.minLength(2)),
  lastName: new FormControl('Drew'),
  email: new FormControl('', [Validators.required, Validators.email])
});

console.log(form.value);
// { firstName: 'Nancy', lastName: 'Drew', email: '' }

console.log(form.status); // 'INVALID' (email is required)
```

### Type Safety

`FormGroup` accepts a generic type argument that describes its inner controls:

```ts theme={null}
interface UserForm {
  firstName: FormControl<string>;
  lastName: FormControl<string>;
  age: FormControl<number>;
}

const form = new FormGroup<UserForm>({
  firstName: new FormControl('', { nonNullable: true }),
  lastName: new FormControl('', { nonNullable: true }),
  age: new FormControl(0, { nonNullable: true })
});

// form.value is typed as { firstName: string, lastName: string, age: number }
```

### Optional Controls

```ts theme={null}
const form = new FormGroup<{
  firstName: FormControl<string>;
  middleName?: FormControl<string>; // Optional
  lastName: FormControl<string>;
}>({
  firstName: new FormControl('', { nonNullable: true }),
  lastName: new FormControl('', { nonNullable: true })
});

// middleName can be added later
form.addControl('middleName', new FormControl('', { nonNullable: true }));
```

## Properties

<ResponseField name="controls" type="TControl">
  A collection of child controls keyed by name.
</ResponseField>

<ResponseField name="value" type="object">
  The aggregate value of the FormGroup, including only enabled controls. Disabled controls are excluded.
</ResponseField>

<ResponseField name="status" type="'VALID' | 'INVALID' | 'PENDING' | 'DISABLED'">
  The validation status of the group. A FormGroup is invalid if any of its children are invalid.
</ResponseField>

<ResponseField name="valid" type="boolean">
  A group is valid when its status is `VALID`.
</ResponseField>

<ResponseField name="invalid" type="boolean">
  A group is invalid when any of its child controls are invalid.
</ResponseField>

<ResponseField name="errors" type="ValidationErrors | null">
  An object containing any errors generated by group-level validators.
</ResponseField>

<ResponseField name="pristine" type="boolean">
  A group is pristine if all of its children are pristine.
</ResponseField>

<ResponseField name="dirty" type="boolean">
  A group is dirty if any of its children are dirty.
</ResponseField>

<ResponseField name="touched" type="boolean">
  A group is touched if any of its children are touched.
</ResponseField>

<ResponseField name="valueChanges" type="Observable<any>">
  A multicasting observable that emits an event every time the value of the group changes.
</ResponseField>

<ResponseField name="statusChanges" type="Observable<FormControlStatus>">
  A multicasting observable that emits an event every time the validation status recalculates.
</ResponseField>

## Methods

### get()

Retrieves a child control given the control's name or path.

<ParamField path="path" type="string | (string | number)[]" required>
  The path to the control. Can be a string or an array for nested paths.
</ParamField>

```ts theme={null}
const form = new FormGroup({
  name: new FormControl(''),
  address: new FormGroup({
    street: new FormControl(''),
    city: new FormControl('')
  })
});

const nameControl = form.get('name');
const streetControl = form.get(['address', 'street']);
const cityControl = form.get('address.city');
```

### setValue()

Sets the value of the FormGroup. It accepts an object that matches the structure of the group.

<ParamField path="value" type="object" required>
  The new value for the group that matches the structure of the group.
</ParamField>

<ParamField path="options.onlySelf" type="boolean" default="false">
  When true, each change only affects this control, and not its parent.
</ParamField>

<ParamField path="options.emitEvent" type="boolean" default="true">
  When true, both the `statusChanges` and `valueChanges` observables emit events.
</ParamField>

```ts theme={null}
const form = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl('')
});

form.setValue({ firstName: 'Nancy', lastName: 'Drew' });
console.log(form.value); // { firstName: 'Nancy', lastName: 'Drew' }

// Throws error if structure doesn't match
// form.setValue({ firstName: 'Nancy' }); // Error!
```

### 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.

<ParamField path="value" type="object" required>
  The object that matches the structure of the group.
</ParamField>

<ParamField path="options.onlySelf" type="boolean" default="false">
  When true, each change only affects this control, and not its parent.
</ParamField>

<ParamField path="options.emitEvent" type="boolean" default="true">
  When true, both the `statusChanges` and `valueChanges` observables emit events.
</ParamField>

```ts theme={null}
const form = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl('')
});

// Can patch partial values
form.patchValue({ firstName: 'Nancy' });
console.log(form.value); // { firstName: 'Nancy', lastName: '' }
```

### reset()

Resets the FormGroup, marking all descendants `pristine` and `untouched`, and resetting the value of all descendants to their default values or null.

<ParamField path="value" type="object" optional>
  Resets the group with an initial value. Can include form state objects with `value` and `disabled` properties.
</ParamField>

<ParamField path="options.onlySelf" type="boolean" default="false">
  When true, each change only affects this control, and not its parent.
</ParamField>

<ParamField path="options.emitEvent" type="boolean" default="true">
  When true, both the `statusChanges` and `valueChanges` observables emit events.
</ParamField>

```ts theme={null}
const form = new FormGroup({
  firstName: new FormControl('Nancy'),
  lastName: new FormControl('Drew')
});

form.reset({ firstName: 'John', lastName: 'Doe' });

// Reset with disabled state
form.reset({
  firstName: { value: 'John', disabled: true },
  lastName: 'Doe'
});
```

### getRawValue()

The aggregate value of the FormGroup, including any disabled controls. Retrieves all values regardless of disabled status.

```ts theme={null}
const form = new FormGroup({
  firstName: new FormControl('Nancy'),
  lastName: new FormControl({ value: 'Drew', disabled: true })
});

console.log(form.value); // { firstName: 'Nancy' }
console.log(form.getRawValue()); // { firstName: 'Nancy', lastName: 'Drew' }
```

### addControl()

Adds a control to this group. Updates the value and validity of the control.

<ParamField path="name" type="string" required>
  The control name to add to the collection.
</ParamField>

<ParamField path="control" type="AbstractControl" required>
  Provides the control for the given name.
</ParamField>

<ParamField path="options.emitEvent" type="boolean" default="true">
  When true, both the `statusChanges` and `valueChanges` observables emit events.
</ParamField>

```ts theme={null}
const form = new FormGroup({
  firstName: new FormControl('')
});

form.addControl('lastName', new FormControl(''));
console.log(form.value); // { firstName: '', lastName: '' }
```

### removeControl()

Removes a control from this group. Updates the value and validity of the control.

<ParamField path="name" type="string" required>
  The control name to remove from the collection.
</ParamField>

<ParamField path="options.emitEvent" type="boolean" default="true">
  When true, both the `statusChanges` and `valueChanges` observables emit events.
</ParamField>

```ts theme={null}
form.removeControl('lastName');
console.log(form.value); // { firstName: '' }
```

### setControl()

Replaces an existing control. If a control with the given name does not exist, it will be added.

<ParamField path="name" type="string" required>
  The control name to replace in the collection.
</ParamField>

<ParamField path="control" type="AbstractControl" required>
  Provides the control for the given name.
</ParamField>

<ParamField path="options.emitEvent" type="boolean" default="true">
  When true, both the `statusChanges` and `valueChanges` observables emit events.
</ParamField>

```ts theme={null}
form.setControl('firstName', new FormControl('New Control'));
```

### contains()

Checks whether there is an enabled control with the given name in the group.

<ParamField path="controlName" type="string" required>
  The control name to check for existence in the collection.
</ParamField>

```ts theme={null}
const hasFirstName = form.contains('firstName'); // true
const hasMiddleName = form.contains('middleName'); // false

// Returns false for disabled controls
form.get('firstName')?.disable();
const hasFirstName = form.contains('firstName'); // false
```

### registerControl()

Registers a control with the group's list of controls. Does not update the value or validity of the control.

```ts theme={null}
const control = new FormControl('');
form.registerControl('newField', control);
```

## Group-Level Validators

You can add validators that consider the value of multiple child controls:

```ts theme={null}
import { AbstractControl, ValidationErrors } from '@angular/forms';

function passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
  const password = group.get('password')?.value;
  const confirmPassword = group.get('confirmPassword')?.value;
  
  return password === confirmPassword ? null : { passwordMismatch: true };
}

const form = new FormGroup(
  {
    password: new FormControl('', Validators.minLength(8)),
    confirmPassword: new FormControl('', Validators.minLength(8))
  },
  { validators: passwordMatchValidator }
);

console.log(form.errors); // { passwordMismatch: true } if passwords don't match
```

## Update Strategies

Set a default `updateOn` value for all child controls:

```ts theme={null}
const form = new FormGroup(
  {
    firstName: new FormControl(''),
    lastName: new FormControl('')
  },
  { updateOn: 'blur' }
);

// All controls will update on blur unless overridden
```

## Examples

### Nested Form Groups

```ts theme={null}
const form = new FormGroup({
  name: new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl('')
  }),
  address: new FormGroup({
    street: new FormControl(''),
    city: new FormControl(''),
    zipCode: new FormControl('')
  })
});

console.log(form.value);
// {
//   name: { firstName: '', lastName: '' },
//   address: { street: '', city: '', zipCode: '' }
// }
```

### Dynamic Form Controls

```ts theme={null}
const form = new FormGroup({
  email: new FormControl('')
});

// Add controls dynamically
function addPhoneNumber() {
  form.addControl('phone', new FormControl(''));
}

// Remove controls dynamically
function removePhoneNumber() {
  form.removeControl('phone');
}
```

### Listening to Value Changes

```ts theme={null}
const form = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl('')
});

form.valueChanges.subscribe(value => {
  console.log('Form value changed:', value);
});

form.patchValue({ firstName: 'Nancy' });
// Logs: Form value changed: { firstName: 'Nancy', lastName: '' }
```

### Conditional Validation

```ts theme={null}
const form = new FormGroup({
  country: new FormControl(''),
  state: new FormControl('')
});

form.get('country')?.valueChanges.subscribe(country => {
  const stateControl = form.get('state');
  
  if (country === 'USA') {
    stateControl?.setValidators(Validators.required);
  } else {
    stateControl?.clearValidators();
  }
  
  stateControl?.updateValueAndValidity();
});
```

## FormRecord

`FormRecord` is a variant of `FormGroup` for dynamic keys:

```ts theme={null}
import { FormRecord, FormControl } from '@angular/forms';

const numbers = new FormRecord({
  home: new FormControl('555-1234')
});

// Add controls with dynamic keys
numbers.addControl('work', new FormControl('555-5678'));
numbers.addControl('mobile', new FormControl('555-9012'));

console.log(numbers.value);
// { home: '555-1234', work: '555-5678', mobile: '555-9012' }
```

## See Also

* [FormControl](/api/forms/form-control) - Track individual form control values
* [FormArray](/api/forms/form-array) - Manage an array of form controls
* [Validators](/api/forms/validators) - Built-in validation functions
* [Reactive Forms Guide](https://angular.dev/guide/forms/reactive-forms)
* [Typed Forms Guide](https://angular.dev/guide/forms/typed-forms)
