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

# Reactive Forms

> Build scalable forms with explicit form control using FormControl, FormGroup, and FormBuilder

Reactive forms provide a model-driven approach to handling form inputs whose values change over time. They use explicit and immutable data structures to manage the state of forms at a given point in time.

## Setting Up Reactive Forms

Import `ReactiveFormsModule` in your component or module:

```typescript theme={null}
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-profile',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `...`
})
export class ProfileComponent {}
```

## FormControl

`FormControl` is the basic building block that tracks the value and validation status of an individual form control.

### Creating a FormControl

```typescript packages/forms/src/model/form_control.ts theme={null}
import { FormControl } from '@angular/forms';

// Simple control with initial value
const name = new FormControl('Nancy');
console.log(name.value); // 'Nancy'

// Control with validation
const email = new FormControl('', Validators.required);
console.log(email.status); // 'INVALID'

// Control with options
const username = new FormControl('', {
  validators: Validators.required,
  updateOn: 'blur'
});
```

### Using FormControl in Templates

```typescript theme={null}
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormControl } from '@angular/forms';

@Component({
  selector: 'app-name',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <label for="name">Name:</label>
    <input id="name" type="text" [formControl]="name">
    <p>Value: {{ name.value }}</p>
  `
})
export class NameComponent {
  name = new FormControl('Nancy');
}
```

### FormControl State and Updates

```typescript packages/forms/src/model/form_control.ts theme={null}
const control = new FormControl('initial');

// Set value
control.setValue('new value');

// Reset to default
control.reset();
console.log(control.value); // null

// Reset to specific value
control.reset('Nancy');
console.log(control.value); // 'Nancy'

// Disable control
control.disable();
console.log(control.status); // 'DISABLED'

// Enable control
control.enable();
```

### Non-Nullable FormControl

```typescript packages/forms/src/model/form_control.ts theme={null}
// By default, controls reset to null
const dog = new FormControl('spot');
dog.reset(); // dog.value is null

// With nonNullable option, controls reset to initial value
const cat = new FormControl('tabby', { nonNullable: true });
cat.reset(); // cat.value is "tabby"
```

## FormGroup

`FormGroup` tracks the value and validity state of a group of `FormControl` instances.

### Creating a FormGroup

```typescript packages/forms/src/model/form_group.ts theme={null}
import { FormGroup, FormControl, Validators } from '@angular/forms';

const profileForm = new FormGroup({
  firstName: new FormControl('', Validators.required),
  lastName: new FormControl('', Validators.required),
  email: new FormControl('', [Validators.required, Validators.email]),
  age: new FormControl(null, [Validators.min(18), Validators.max(100)])
});

console.log(profileForm.value);
// { firstName: '', lastName: '', email: '', age: null }
```

### Using FormGroup in Templates

```typescript theme={null}
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'app-profile',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
      <label for="firstName">First Name:</label>
      <input id="firstName" type="text" formControlName="firstName">
      
      <label for="lastName">Last Name:</label>
      <input id="lastName" type="text" formControlName="lastName">
      
      <label for="email">Email:</label>
      <input id="email" type="email" formControlName="email">
      
      <button type="submit" [disabled]="profileForm.invalid">Submit</button>
    </form>
    
    <p>Form Status: {{ profileForm.status }}</p>
    <p>Form Value: {{ profileForm.value | json }}</p>
  `
})
export class ProfileComponent {
  profileForm = new FormGroup({
    firstName: new FormControl('', Validators.required),
    lastName: new FormControl('', Validators.required),
    email: new FormControl('', [Validators.required, Validators.email])
  });
  
  onSubmit() {
    if (this.profileForm.valid) {
      console.log('Form submitted:', this.profileForm.value);
    }
  }
}
```

### Nested FormGroups

```typescript packages/forms/src/model/form_group.ts theme={null}
const userForm = new FormGroup({
  name: new FormControl(''),
  address: new FormGroup({
    street: new FormControl(''),
    city: new FormControl(''),
    state: new FormControl(''),
    zip: new FormControl('')
  })
});

console.log(userForm.value);
// { name: '', address: { street: '', city: '', state: '', zip: '' } }
```

Template with nested FormGroup:

```html theme={null}
<form [formGroup]="userForm">
  <input formControlName="name" placeholder="Name">
  
  <div formGroupName="address">
    <input formControlName="street" placeholder="Street">
    <input formControlName="city" placeholder="City">
    <input formControlName="state" placeholder="State">
    <input formControlName="zip" placeholder="ZIP">
  </div>
</form>
```

### FormGroup Methods

```typescript packages/forms/src/model/form_group.ts theme={null}
const form = new FormGroup({
  first: new FormControl('Nancy'),
  last: new FormControl('Drew')
});

// Set all values (strict)
form.setValue({ first: 'John', last: 'Doe' });

// Patch partial values
form.patchValue({ first: 'Jane' });
console.log(form.value); // { first: 'Jane', last: 'Doe' }

// Reset form
form.reset();
console.log(form.value); // { first: null, last: null }

// Get raw value (includes disabled controls)
form.getRawValue();

// Add control dynamically
form.addControl('middle', new FormControl(''));

// Remove control
form.removeControl('middle');

// Check if control exists and is enabled
form.contains('first'); // true
```

## FormArray

`FormArray` tracks the value and validity state of an array of controls. Use it for dynamic forms where the number of controls is not known in advance.

### Creating a FormArray

```typescript packages/forms/src/model/form_array.ts theme={null}
import { FormArray, FormControl, Validators } from '@angular/forms';

const hobbies = new FormArray([
  new FormControl('Reading'),
  new FormControl('Gaming')
]);

console.log(hobbies.value); // ['Reading', 'Gaming']
console.log(hobbies.length); // 2
```

### Dynamic Form Example

```typescript theme={null}
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormArray, FormControl, FormGroup } from '@angular/forms';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-contact-list',
  standalone: true,
  imports: [ReactiveFormsModule, CommonModule],
  template: `
    <form [formGroup]="contactForm">
      <div formArrayName="contacts">
        <div *ngFor="let contact of contacts.controls; let i = index">
          <label>Contact {{ i + 1 }}:</label>
          <input [formControlName]="i" placeholder="Phone number">
          <button type="button" (click)="removeContact(i)">Remove</button>
        </div>
      </div>
      <button type="button" (click)="addContact()">Add Contact</button>
    </form>
    <p>Contacts: {{ contacts.value | json }}</p>
  `
})
export class ContactListComponent {
  contactForm = new FormGroup({
    contacts: new FormArray([
      new FormControl('')
    ])
  });
  
  get contacts() {
    return this.contactForm.get('contacts') as FormArray;
  }
  
  addContact() {
    this.contacts.push(new FormControl(''));
  }
  
  removeContact(index: number) {
    this.contacts.removeAt(index);
  }
}
```

### FormArray Methods

```typescript packages/forms/src/model/form_array.ts theme={null}
const arr = new FormArray([
  new FormControl('Nancy'),
  new FormControl('Drew')
]);

// Access control at index
const first = arr.at(0);
console.log(first.value); // 'Nancy'

// Add control to end
arr.push(new FormControl('John'));

// Insert control at index
arr.insert(1, new FormControl('Jane'));

// Remove control at index
arr.removeAt(0);

// Replace control at index
arr.setControl(0, new FormControl('Bob'));

// Clear all controls
arr.clear();
console.log(arr.length); // 0

// Set all values
arr.setValue(['Alice', 'Bob', 'Charlie']);

// Patch values (can be partial)
arr.patchValue(['David']);
```

## FormBuilder

The `FormBuilder` service provides convenience methods to reduce boilerplate when creating forms.

### Basic Usage

```typescript packages/forms/src/form_builder.ts theme={null}
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-profile',
  template: `...`
})
export class ProfileComponent {
  constructor(private fb: FormBuilder) {}
  
  // Without FormBuilder
  profileForm1 = new FormGroup({
    firstName: new FormControl(''),
    lastName: new FormControl(''),
    email: new FormControl('')
  });
  
  // With FormBuilder - much cleaner!
  profileForm2 = this.fb.group({
    firstName: [''],
    lastName: [''],
    email: ['']
  });
  
  // With validators
  profileForm3 = this.fb.group({
    firstName: ['', Validators.required],
    lastName: ['', Validators.required],
    email: ['', [Validators.required, Validators.email]]
  });
}
```

### FormBuilder with Nested Groups and Arrays

```typescript packages/forms/src/form_builder.ts theme={null}
import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

@Component({
  selector: 'app-order',
  template: `...`
})
export class OrderComponent {
  constructor(private fb: FormBuilder) {}
  
  orderForm = this.fb.group({
    customerName: ['', Validators.required],
    email: ['', [Validators.required, Validators.email]],
    address: this.fb.group({
      street: [''],
      city: [''],
      state: [''],
      zip: ['', Validators.pattern(/^\d{5}$/)]
    }),
    items: this.fb.array([
      this.fb.group({
        product: [''],
        quantity: [1, Validators.min(1)],
        price: [0]
      })
    ])
  });
  
  get items() {
    return this.orderForm.get('items') as FormArray;
  }
  
  addItem() {
    this.items.push(this.fb.group({
      product: [''],
      quantity: [1, Validators.min(1)],
      price: [0]
    }));
  }
}
```

### NonNullableFormBuilder

```typescript packages/forms/src/form_builder.ts theme={null}
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-settings',
  template: `...`
})
export class SettingsComponent {
  constructor(private fb: FormBuilder) {}
  
  // Regular FormBuilder - resets to null
  settingsForm1 = this.fb.group({
    theme: ['dark']
  });
  // After reset: { theme: null }
  
  // NonNullable FormBuilder - resets to initial value
  settingsForm2 = this.fb.nonNullable.group({
    theme: ['dark']
  });
  // After reset: { theme: 'dark' }
}
```

## Reactive Forms Best Practices

<AccordionGroup>
  <Accordion title="Use FormBuilder for cleaner code">
    FormBuilder reduces boilerplate and makes your form creation code more readable.
  </Accordion>

  <Accordion title="Leverage TypeScript for type safety">
    ```typescript theme={null}
    interface ProfileForm {
      firstName: FormControl<string>;
      lastName: FormControl<string>;
      email: FormControl<string>;
    }

    const form = new FormGroup<ProfileForm>({
      firstName: new FormControl('', { nonNullable: true }),
      lastName: new FormControl('', { nonNullable: true }),
      email: new FormControl('', { nonNullable: true })
    });
    ```
  </Accordion>

  <Accordion title="Create reusable form components">
    Extract complex form sections into their own components with `ControlValueAccessor`.
  </Accordion>

  <Accordion title="Subscribe to valueChanges strategically">
    ```typescript theme={null}
    this.form.get('email')?.valueChanges.subscribe(email => {
      // React to email changes
    });
    ```
  </Accordion>

  <Accordion title="Use updateOn for performance">
    ```typescript theme={null}
    const form = this.fb.group({
      description: ['', { updateOn: 'blur' }]
    });
    ```
  </Accordion>
</AccordionGroup>

## Observing Form State

```typescript theme={null}
import { Component, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-reactive-form',
  template: `...`
})
export class ReactiveFormComponent implements OnInit {
  form = this.fb.group({
    name: [''],
    email: ['']
  });
  
  constructor(private fb: FormBuilder) {}
  
  ngOnInit() {
    // Listen to value changes
    this.form.valueChanges.subscribe(value => {
      console.log('Form value changed:', value);
    });
    
    // Listen to status changes
    this.form.statusChanges.subscribe(status => {
      console.log('Form status changed:', status);
    });
    
    // Listen to specific control
    this.form.get('email')?.valueChanges.subscribe(email => {
      console.log('Email changed:', email);
    });
  }
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Form Validation" icon="shield-check" href="/forms/validation">
    Learn about built-in and custom validators
  </Card>

  <Card title="Template-Driven Forms" icon="file-code" href="/forms/template-driven">
    Compare with template-driven approach
  </Card>
</CardGroup>
