Build the Form Model in TypeScript
Reactive Forms
Define FormControl, FormGroup, and FormArray in the class. The template binds to that model with formControlName and formGroup.
What you'll learn
- Create a FormGroup with FormControls
- Bind controls in the template
- Read and write values from code
Reactive forms put the form definition in code. That gives you typed values, testable logic, and easy dynamism.
The Manual Way
import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-login',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="save()">
<input formControlName="email" placeholder="Email" />
<input formControlName="password" type="password" />
<button>Sign in</button>
</form>
`,
})
export class LoginComponent {
form = new FormGroup({
email: new FormControl(''),
password: new FormControl(''),
});
save() {
console.log(this.form.value);
}
} With FormBuilder
FormBuilder is a service that makes the construction terser.
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
@Component({
selector: 'app-login',
standalone: true,
imports: [ReactiveFormsModule],
template: `<form [formGroup]="form">...</form>`,
})
export class LoginComponent {
private fb = inject(FormBuilder);
form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
});
} Reading and Writing
form.value— current data objectform.valid/form.invalidform.controls.email— the individual controlform.patchValue({ email: 'a@b.com' })— update some fieldsform.setValue({...})— update all fields, type-checkedform.reset()— back to initial stateform.valueChanges— observable of every change
The reactive API stays out of the DOM. You can test a form by instantiating its class and asserting on form.value — no fixtures needed.