FormControl, FormGroup, FormArray
Three building blocks of reactive forms: single input (FormControl), object group (FormGroup), and dynamic array (FormArray). Combine them for any form structure.
FormControl is the basic building block โ it tracks the value and validation state of a single input field.
import { FormControl, Validators } from '@angular/forms';
// FormControl(initialValue, validators)
const name = new FormControl('Biryani', [
Validators.required,
Validators.minLength(3)
]);
// Properties
name.value // 'Biryani'
name.valid // true (passes required and minLength)
name.errors // null (no errors)
name.setValue('Chicken Biryani'); // Programmatically set
"FormControl = ek plate โ ek dish ka data."
Every FormControl holds:
- A current value
- Validation state (valid/invalid/errors)
- Interaction state (touched/dirty/pristine)
- An Observable of changes (valueChanges)
FormGroup groups multiple FormControls into one object โ like a form with multiple fields.
import { FormGroup, FormControl, Validators } from '@angular/forms';
const biryaniForm = new FormGroup({
name: new FormControl('', [Validators.required]),
price: new FormControl(0, [Validators.min(1)]),
category: new FormControl('chicken'),
});
"FormGroup = full thaali โ multiple plates ek saath."
Template:
<form [formGroup]="biryaniForm">
<input formControlName="name" placeholder="Biryani name" />
<input type="number" formControlName="price" />
<select formControlName="category">
<option value="chicken">Chicken</option>
<option value="mutton">Mutton</option>
</select>
</form>
formGroup binds the FormGroup to the form element. formControlName binds each input to a control by its key name.
biryaniForm.value // { name: '', price: 0, category: 'chicken' }
biryaniForm.valid // true only if ALL controls are validFormArray manages a dynamic list of controls โ like adding multiple side orders.
import { FormArray, FormControl } from '@angular/forms';
const extras = new FormArray([
new FormControl('Raita'),
new FormControl('Salan'),
]);
"FormArray = side orders โ jitne chahiye utne add karo."
Adding and removing dynamically:
// Add a new control
extras.push(new FormControl('Mirchi'));
// Remove at index
extras.removeAt(0);
// Clear all
extras.clear();
// Insert at position
extras.insert(1, new FormControl('Salad'));
// Get length
extras.length; // 3 (after push)
Template:
<div formArrayName="extras">
@for (item of extras.controls; track $index) {
<div>
<input [formControl]="item" />
<button (click)="extras.removeAt($index)">Remove</button>
</div>
}
</div>
Use cases: multiple phone numbers, dynamic tags, order items, team members.
You can nest FormGroups inside FormGroups and FormArrays inside FormGroups โ creating complex form structures.
const orderForm = new FormGroup({
customerName: new FormControl(''),
// Nested FormGroup
address: new FormGroup({
street: new FormControl(''),
city: new FormControl('Hyderabad'),
pincode: new FormControl(''),
}),
// FormArray
orders: new FormArray([
new FormGroup({
item: new FormControl('', Validators.required),
qty: new FormControl(1, [Validators.min(1)]),
})
]),
});
"FormGroup ke andar FormGroup, FormArray ke andar FormGroup โ matryoshka."
Template:
<form [formGroup]="orderForm">
<input formControlName="customerName" />
<div formGroupName="address">
<input formControlName="street" />
<input formControlName="city" />
<input formControlName="pincode" />
</div>
<div formArrayName="orders">
@for (order of orders.controls; track $index) {
<div [formGroupName]="$index">
<input formControlName="item" />
<input formControlName="qty" />
</div>
}
</div>
</form>
Access nested values with dot notation: this.form.get('address.city')?.value
You can read and write values programmatically.
Getting values:
// Direct property
this.biryaniForm.value.name;
// Via get() โ returns AbstractControl | null
this.biryaniForm.get('name')?.value;
// Nested
this.biryaniForm.get('address.city')?.value;
// FormArray item
this.biryaniForm.get('orders.0.item')?.value;
Setting values:
// Set a single control
this.biryaniForm.get('name')?.setValue('Chicken Dum Biryani');
// Partial update โ only specified fields change
this.biryaniForm.patchValue({
name: 'Mutton Biryani',
price: 399
});
// Full update โ ALL fields required, throws error if missing
this.biryaniForm.setValue({
name: 'Chicken Biryani',
price: 299,
category: 'chicken'
});
"patchValue = partial update, setValue = full replace."
setValueโ requires ALL controls in the group, throws error if any missingpatchValueโ accepts partial object, only updates specified controlsresetโ resets to initial values (or specified defaults)
Key Takeaways
- โ FormControl โ single input (value, validators, state)
- โ FormGroup โ object of controls, bind via [formGroup] + formControlName
- โ FormArray โ dynamic list, push()/removeAt()/clear() at runtime
- โ Nest any combination: FormGroup โ FormGroup, FormGroup โ FormArray, FormArray โ FormGroup
- โ setValue() requires ALL fields (strict), patchValue() accepts partial (flexible)
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login