[Fuente]

Los formularios reactivos en Angular también se conocen como formularios dirigidos por modelos, los formularios se diseñan en el componente (usando FormBuilder y el método group) y luego se realizan los enlaces para el HTML usando la inyección de dependencias en el constructor.

Formularios reactivos

Antes de empezar hay que añadir en [src/app/app.module.ts] la importación del módulo ReactiveFormsModule:

import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";

import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";

import { ReactiveFormsModule } from "@angular/forms"; // <---

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AppRoutingModule, ReactiveFormsModule], // <----
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}

Diseño de formulario en la vista

La clase FormGroup agrupa los controles FormControl de un formulario en un objeto, angForm es un atributo de la clase AppComponent, cuando se instancia la clase FormGroup podemos pasar como argumento una colección de controles hijo.

<div style="text-align:center">
  <h1>Welcome to {{ title }}!!</h1>
  <form [formGroup]="angForm" novalidate>
    <div class="form-group">
      <label class="center-block"
        >Name:
        <input class="form-control" formControlName="name" />
      </label>
    </div>
    <div
      *ngIf="
        angForm.controls['name'].invalid &&
        (angForm.controls['name'].dirty || angForm.controls['name'].touched)
      "
      class="alert alert-danger"
    >
      <div *ngIf="angForm.controls['name'].errors.required">
        Name is required.
      </div>
    </div>
  </form>
  <p>Form value: {{ angForm.value | json }}</p>
  <p>Form status: {{ angForm.status | json }}</p>
</div>

Mostrando el estado del formulario

El estado inicial es no valido. El estado global del formulario es accesible a través del atributo status de tipo string de la clase FormGroup. Usando la técnica de bata binding por interpolación del modelo a la vista se muestra en todo momento el estado del formulario.

<p>Form status: {{ angForm.status | json }}</p>

Mensajes de error personalizados

Condicionamos que se muestre este mensaje de error con una condicional ngIf accediendo al control que queremos comprobar:

      <div *ngIf="angForm.controls['name'].errors.required">
        Name is required.
      </div>

Modelo

[src/app/app.component.ts]

import { Component } from "@angular/core";
import { FormGroup, FormBuilder, Validators } from "@angular/forms";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  title = "Angular Form Validation Tutorial";
  angForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.createForm();
  }

  createForm() {
    this.angForm = this.fb.group({
      name: ["", Validators.required]
    });
  }
} // FIN class

Como atributo de la clase se declara angForm que es una instancia de la clase FormGroup.

El servicio FormBuilder se inyecta en el constructor de la clase, el constructor llama a un método interno createForm.

Campos obligatorios

El control más habitual en los controles de un formulario es que los datos a introducir sean obligatorios. FormBuilder tiene el método group (azucarillo sintáctico) para facilitar la creación de instancias FormControl, como parámetro de le pasa una colección con los controles hijo.

La clase Validators que hemos importado en la cabecera tiene ya métodos para las validaciones más típicas: min, max, email, maxLength, required, etc.

Nota: En HTML5 existe el atributo required para el mismo propósito:

<input class="form-control" formControlName="name" required />

Botón para procesar el formulario

Aún falta el típico botón de submit, modifico la etiqueta form de la vista.

La directiva FormGroup escucha el evento submit emitido por el elemento form y emite un evento ngSubmit que asociamos a una función de callback.

Añadimos el capturador de evento en la etiqueta form:

<form [formGroup]="angForm" (ngSubmit)="onSubmit()" novalidate>
  onSubmit() {
    if (this.angForm.valid) {
      console.log(this.angForm.value);
    } else {
      alert("FILL ALL FIELDS");
    }
  }

Suscripción en cambios de estado y valor

Las clases FormControl, FormGroup, y FormArray definen un observable al que podemos suscribirnos para controlar cualquier cambio de estado.

En el método createForm() añadimos la siguiente línea:

this.angForm.controls["name"].valueChanges.subscribe(data => {
  console.log(data);
});

Apellido obligatorio

Añadir nuevos campos es trivial, solo tenemos que definirlos en la vista:

<input class="form-control" formControlName="surname" />

Y en el método createForm():

this.angForm = this.fb.group({
  name: ["", Validators.required],
  surname: ["", Validators.required]
});

Añadiendo nuevas restricciones

Voy a controlar la longitud máximo del nombre, debe ser como mucho de longitud 5.

  createForm() {
    this.angForm = this.fb.group({
      name: ["", [Validators.required, Validators.maxLength(5)]],
      surname: ["", Validators.required]
    });
  }

Código fuente del proyecto

  • En mi repositorio GitLab: angular / src / ui / forms / ngValidation.

En StackBlitz:

Enlaces externos