Theory
Forms as data you control
FestConnect's registration form is more than input boxes: it must validate names and emails, let a team add a variable number of members, block submission until everything is valid, and then send the data to the backend. Angular's reactive forms handle all of this by modelling the form as data in your TypeScript code, which you can inspect, validate, and change programmatically.
This lesson covers the reactive-forms building blocks, FormControl, FormGroup, FormArray, and validators, plus how to handle errors and submit to an API. It is the front-end skill you will use on every real form.
Theory
FormControl, FormGroup, FormArray
Three classes build a reactive form.
A FormControl represents a single field (the name box), holding its value and validation state. A FormGroup bundles several controls into one form (name, email, phone). A FormArray is a dynamic list of controls, perfect when the number of fields is not fixed, like a team registration where members can be added or removed at runtime.
Because the form lives in your class as these objects, you can read its value, check validity, add a FormArray entry, or reset it, all in code, which is what makes reactive forms powerful for real applications.
Practical
A reactive form with a FormArray
import { FormGroup, FormControl, FormArray, Validators } from '@angular/forms';
form = new FormGroup({
name: new FormControl('', [Validators.required]),
email: new FormControl('', [Validators.required, Validators.email]),
members: new FormArray([]), // a dynamic, growing list of controls
});
addMember() {
this.form.controls.members.push(new FormControl('', Validators.required));
}
// members starts empty; addMember() appends a new required field at runtime.Theory
Validators, errors, and submitting
Validators decide whether input is acceptable. Built-in ones include Validators.required and Validators.email. A custom validator is a function you write that returns an error object (or null if valid), for rules like 'must be a college email'. An async validator returns an Observable or Promise, used when validation needs the server, such as checking a username is not already taken.
For error handling, you read a control's errors, invalid, and touched state to show messages only after the user has interacted. On submit, you check form.valid, then send form.value to your API with Angular's HttpClient. Validate in the form, submit only when valid.
Practical
Submitting the form to an API when valid
constructor(private http: HttpClient) {}
submit() {
if (this.form.invalid) {
this.form.markAllAsTouched(); // reveal validation errors
return;
}
// form.value is a plain object ready to send
this.http.post('/api/register', this.form.value)
.subscribe(() => console.log('Registered!'));
}
// A custom validator example (returns an error object or null):
// const collegeEmail = (c: AbstractControl) =>
// c.value?.endsWith('@college.edu') ? null : { collegeEmail: true };Quiz
In a reactive form, which class lets you build a dynamic list of fields that the user can add to or remove at runtime (like adding team members)?
- FormControl, because it is a single field
- FormArray, a dynamic list of controls you can add to or remove from at runtime
- FormGroup, because it groups fixed fields
- Validators, because they control the count
Show the answer
FormArray, a dynamic list of controls you can add to or remove from at runtime
FormArray represents a dynamic list of controls that you can push to or remove from at runtime, exactly what a variable number of team members needs. Option A, FormControl, is a single field, not a list. Option C, FormGroup, bundles a FIXED set of named controls (name, email, phone) into one form; it is not designed for a growing/shrinking list. Option D, Validators, checks whether input is valid; it has nothing to do with the number of fields. Use FormControl for one field, FormGroup for a fixed set, and FormArray for a dynamic list.
Think first
Why are reactive forms preferred over template-driven forms for real apps?
Angular also has template-driven forms defined in the HTML. Why do larger apps favour reactive forms? Then tap.
Show the answer
Because reactive forms put the form's structure and logic in your TYPESCRIPT code, which makes them more explicit, testable, and powerful for the complex forms real apps need. In a template-driven form, the form is built implicitly from directives in the HTML template, which is quick for a tiny form but becomes hard to manage as complexity grows: dynamic fields, cross-field rules, and custom validation are awkward to express in markup, and there is no single place to see the whole form's shape. Reactive forms flip this: you define FormControl, FormGroup, and FormArray in the component class, so the entire form is a data structure you can read, manipulate, and reason about directly, add a field with a line of code (FormArray.push), attach a custom or async validator as a plain function, check validity and errors programmatically, and reset or patch values at will. This explicitness also makes them far easier to UNIT TEST, because you can create and drive the form in code without rendering a template. And it scales: the same patterns handle a two-field login and a fifty-field multi-step wizard. Template-driven forms still have their place for very simple cases, but for the dynamic, validated, API-submitting forms in an app like FestConnect, the control and testability of reactive forms win. Form as data in code beats form as markup for anything non-trivial.
Summary
Key takeaways
- Reactive forms model a form as data in the component class, so you can inspect, validate, and change it in code.
- FormControl is one field; FormGroup bundles a fixed set of controls; FormArray is a dynamic list you can add to or remove from.
- Validators check input: built-in (Validators.required, Validators.email), custom (a function returning an error or null), and async (returns an Observable/Promise, e.g. server checks).
- Handle errors via a control's errors/invalid/touched state, showing messages after interaction.
- On submit, check form.valid, then send form.value to an API with HttpClient.
- Reactive forms suit real apps because they are explicit, testable, and handle dynamic and custom validation well.
- Memory hook: FormControl/Group/Array for structure, validators for rules, form.value to the API.