Forms are where users give your app information: signing up, searching, paying. Angular has two form systems. This lesson starts with reactive forms, which define the form in your class so that it is easy to test and validate.
What you will learn
- How to define a form with
FormGroupandFormControl - How to connect the form to a template
- How to submit, read values and reset the form
Set up
Reactive forms live in @angular/forms, which is already installed in a new project. You import ReactiveFormsModule in the components that use it.
Define the form
A form is a group of controls. Each control holds one value. The FormBuilder makes creating them short. Its nonNullable helper means a control resets to its starting value instead of to null, and it gives you simpler types:
// src/app/contact-form/contact-form.ts
import { Component, inject, signal } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-contact-form',
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="submit()">
<p>
<label for="name">Name</label><br />
<input id="name" formControlName="name" />
</p>
<p>
<label for="email">Email</label><br />
<input id="email" type="email" formControlName="email" />
</p>
<p>
<label for="message">Message</label><br />
<textarea id="message" formControlName="message" rows="3"></textarea>
</p>
<button type="submit">Send</button>
<button type="button" (click)="form.reset()">Clear</button>
</form>
@if (sent()) {
<p role="status">Thanks, {{ sent()?.name }}. We will reply to {{ sent()?.email }}.</p>
}
`,
})
export class ContactForm {
private readonly fb = inject(FormBuilder);
protected readonly form = this.fb.nonNullable.group({
name: '',
email: '',
message: '',
});
protected readonly sent = signal<{ name: string; email: string } | null>(null);
protected submit(): void {
const value = this.form.getRawValue();
this.sent.set({ name: value.name, email: value.email });
this.form.reset();
}
}
// src/app/app.ts
import { Component } from '@angular/core';
import { ContactForm } from './contact-form/contact-form';
@Component({
imports: [ContactForm],
selector: 'app-root',
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {}
<!-- src/app/app.html -->
<main>
<h1>Contact us</h1>
<app-contact-form />
</main>
How the pieces connect
[formGroup]="form"links the<form>element to the group in your class.formControlName="name"links an input to the control with that name. The name is plain text, so there are no brackets.(ngSubmit)="submit()"runs when the form is submitted, whether by the button or the Enter key.form.reset()puts every control back to its starting value.
Reading values
Inside the class, form.value and form.getRawValue() give you an object with the current values. Because the form was built with nonNullable, the values are typed as strings. Use getRawValue() when you want every control, including disabled ones.
You can also read and change a single control:
// read a control's value
const email = this.form.controls.email.value;
// change values from code
this.form.patchValue({ name: 'Alex' });
patchValue updates some controls, and setValue requires all of them.
Why use a FormGroup at all?
The group tracks the state of the whole form for you: whether it is valid, whether it has been touched or changed, and what its values are. In the next lesson you will use that state to show helpful validation messages.
Common mistakes
- Forgetting `ReactiveFormsModule`: you will get an error that
formGroupis not a known property. - Mismatched control names:
formControlName="mail"when the control isemailfails at runtime. Check spelling. - Writing `[formControlName]`: the name is a string, so use it without brackets.
Try it yourself
- Add a
subjectcontrol and field. - Add a button that fills the form with sample data using
patchValue. - Show the current form value on the page with
{{ form.value | json }}(remember to importJsonPipe).
Quick check
- What does
formControlNamedo? - Which method puts every control back to its starting value?
- What event runs when the form is submitted?
Next, make sure users enter sensible data with form validation. Reference: the reactive forms guide.