Official Documentation View Source on Github

Highlights

  1. Use FormBuilder and bind it to the form with the [formGroup] attribute.
  2. Use the formControlName attribute to bind each field to its form control.
  3. Create one getter per form control for easier access.
  4. Access each field validation errors through its getter.
  5. Access the form validation state through the form.

// app/simple-form/user.ts

export interface User {
  id: number;
  name: string;
  birthdate: Date;
  favoriteColor?: string;
}
<!-- app/simple-form/user-form-reactive/user-form-reactive.component.html -->

<h1>User Form (reactive)</h1>

<mat-card>
  <form [formGroup]="userForm" (ngSubmit)="submit()">
    <mat-form-field>
      <mat-label>Name</mat-label>
      <input matInput name="name" formControlName="name" />
      <mat-error *ngIf="name?.errors?.required"
        >You must enter a value</mat-error
      >
    </mat-form-field>
    <mat-form-field>
      <mat-label>Birth date</mat-label>
      <input
        matInput
        name="birthdate"
        formControlName="birthdate"
        [matDatepicker]="picker"
      />
      <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
      <mat-datepicker #picker></mat-datepicker>
      <mat-error *ngIf="birthdate?.errors?.required"
        >You must enter a value</mat-error
      >
    </mat-form-field>
    <mat-form-field>
      <mat-label>Favorite color</mat-label>
      <mat-select name="favoriteColor" formControlName="favoriteColor">
        <mat-option *ngFor="let color of colors" [value]="color">{{
          color
        }}</mat-option>
      </mat-select>
    </mat-form-field>
    <div class="form-buttons">
      <button
        mat-raised-button
        type="submit"
        color="primary"
        [disabled]="loading || !userForm.valid"
      >
        Submit
      </button>
    </div>
  </form>
</mat-card>

<h2>User (JSON)</h2>

<pre
  >{{ userForm.value | json }}
</pre>
// app/simple-form/user-form-reactive/user-form-reactive.component.ts

import { Component } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { finalize } from 'rxjs/operators';
import { User } from 'src/app/simple-form/user';
import { UserService } from 'src/app/simple-form/user.service';

@Component({
  selector: 'app-user-form-reactive',
  templateUrl: './user-form-reactive.component.html',
})
export class UserFormReactiveComponent {
  readonly colors = ['Red', 'Green', 'Blue'];
  readonly userForm = this.formBuilder.group({
    name: ['', Validators.required],
    birthdate: ['', Validators.required],
    favoriteColor: [''],
  });
  loading = false;

  get name() {
    return this.userForm.get('name');
  }

  get birthdate() {
    return this.userForm.get('birthdate');
  }

  constructor(
    private readonly formBuilder: FormBuilder,
    private readonly userService: UserService,
    private readonly snackBar: MatSnackBar
  ) {}

  submit(): void {
    const user: User = this.userForm.value;

    this.loading = true;
    this.userService
      .save(user)
      .pipe(finalize(() => (this.loading = false)))
      .subscribe(({ id }) => {
        this.snackBar.open(`User saved with ID #${id}`, 'Close');
      });
  }
}
// app/simple-form/user.service.ts

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { User } from 'src/app/simple-form/user';

export interface UserSaveResponse {
  id: number;
}

@Injectable({
  providedIn: 'root',
})
export class UserService {
  save(user: User): Observable<UserSaveResponse> {
    console.log('Save:', user);
    return of({
      id: 1,
    }).pipe(delay(1000));
  }
}