Formularios

ReactiveFormsModule

FormsModule

La validación se hace en el DOM

La validación se hace en el TypeScript

FormsModule

Para usar FormsModule primero hay que importarlo y declararlo en un módulo.

El FormsModule pertenece a "@angular/forms".

<!-- app.module.ts -->
import { FormsModule } from "@angular/forms"

@NgModule({
    ...
    imports: [FormsModule],
    ...
})

Los campos (inputs, textareas, selects, etc.) se deben vincular a "ngModel" y tener seteada la propiedad "name"

<input type="text" name="nombre" ngModel>

Los formularios se vinculan a través de referencias locales.

<form #f>
...
</form>

Pero una mejor forma es hacer que la referencia local herede de la clase "NgForm"

<form #f="ngForm">
...
</form>

El envío de los datos ingresados en los campos se hará a través del evento "ngSubmit".

<form #f="ngForm" (ngSubmit)="grabar()">
...
</form>

Con FormsModule, los validadores se aplican directamente al DOM.

<input type="text" name="nombre" ngModel required>

<input type="email" name="correo" ngModel required email>

<input type="tel" name="telefono" ngModel required min(7) max(9)>

El formulario completo se puede validar a través de la propiedad "valid".

<form #f="ngForm" (ngSubmit)="grabar()">
...

    <button [disabled]="!f.valid">Grabar</button>
</form>

Cuando un campo no es válido, Angular le agrega algunas clases. Las que necesitaremos son "ng-invalid" y "ng-touched".

<form ...>
...

    <input class="ng-invalid ng-touched" ...>
</form>

Podemos crear una regla de estilos para resaltar el campo no válido.

input.ng-invalid.ng-touched {
    border: 1px solid red
}

No solo se puede validar el formulario completo. También podemos validar cada campo a través de una referencia local.

<form ...>
...

    <input ... #campo="ngModel">
</form>

También podemos manejar los mensajes de error de cada campo.

<form ...>
...

    <input ... #campo="ngModel">
    <span *ngIf="!campo.valid && campo.touched">
        Campo no válido
    </span>
</form>

Puede haber comunicación bi-direccional a través de "ngModel".

<form ...>
...

    <input ... [(ngModel)]="propiedad">
</form>

Se pueden crear grupos de validación a través de "ngModelGroup".

<form ...>
    <div ngModelGroup="dataUsuario" 
        #dataUsuario="ngModelGroup">
        <input type="text" name="nombre" ngModel>
    </div>
    <span *ngIf="!dataUsuario.valid && dataUsuario.touched">
        El formulario no es válido
    </span>
</form>

Validación de radiobuttons.

<!-- app.component.ts -->
...
export class AppComponent {
    generos: string[] = ["hombre", "mujer"]
    ...
}
<!-- app.component.html -->
...
<label *ngFor="let genero of generos">
   <input type="radio" name="genero" 
        ngModel [value]="genero" required>
        {{genero}}
</label>

Uso de "setValue"

<!-- app.component.ts -->
...
export class AppComponent {
    @ViewChild("f") registro: NgForm

    ngOnInit(){
        this.registro.setValue({
            "nombre": "Nombre Persona",
            "apellido": "Apellido Persona"
        })
    }
}

Uso de "patchValue"

<!-- app.component.ts -->
...
export class AppComponent {
    @ViewChild("f") registro: NgForm

    ngOnInit(){
        this.registro.form.patchValue({
            "nombre": "Nombre Persona"
        })
    }
}

Uso de "reset"

<!-- app.component.ts -->
...
export class AppComponent {
    @ViewChild("f") registro: NgForm

    grabar(){
        ...
        this.registro.reset({
            "nombre": "Nombre Persona",
            "apellido": "Apellido Persona"
        })
    }
}

Tarea 1

- Crear un formulario de registro con los siguientes campos:

nombre completo, género, correo y contraseña.

- El nombre completo es requerido y no puede estar en blanco.

- El usuario seleccionará género a través de un select.

- El correo es requerido, debe ser un correo y  no debe estar en blanco. Mostrar mensajes de error diferentes dependiendo del error.

- Debe haber un botón de envío  el cual no enviará nada a menos que  el formulario sea válido. Si lo es, imprimirá en consola todos los campos.

ReactiveFormsModule

Para usar ReactiveFormsModule primero hay que importarlo y declararlo en un módulo.

El ReactiveFormsModule pertenece a "@angular/forms".

<!-- app.module.ts -->
import { ReactiveFormsModule } from "@angular/forms"

@NgModule({
    ...
    imports: [ReactiveFormsModule],
    ...
})

Con ReactiveFormsModule se usan FormGroup y FormControl.

<!-- app.component.ts -->
...
export class AppComponent {
    ...
    grupo: FormGroup

    ngOnInit(){
        this.grupo = new FormGroup({
            "nombre": new FormControl(null, Validators.required),
            "apellido": new FormControl("Apellido Persona",
                                        Validators.required),
            "correo": new FormControl("Correo Persona", 
                        [Validators.required, Validators.email])
        })
    }
}

Con ReactiveFormsModule se usan FormGroup y FormControl.

<!-- app.component.html -->
...
<form [formGroup]="grupo">
    <input type="text" formControlName="nombre">
    <input type="text" formControlName="apellido">
    <button [disabled]="!grupo.valid">Grabar</button>
</form>
...

Para validar se usa "Validators" el cual forma parte de "@angular/forms"

import { Validators } from "@angular/forms" 

Para manejar mensajes de error se usa el método "get".

<!-- app.component.html -->
...
<form [formGroup]="grupo">
    <input type="text" formControlName="nombre">
    <span 
        *ngIf="!grupo.get('nombre').valid 
                    && grupo.get('nombre').touched">
        Error
    </span>
    <input type="text" formControlName="apellido">
    <button [disabled]="!grupo.valid">Grabar</button>
</form>
...

Tarea 2

- Lo mismo que la tarea 1 pero usando ReactiveFormsModule.

Creación de validador

Supongamos que tenemos el siguiente formulario y necesitamos que el correo sea ingresado y además tenga formato de correo.

<form [formGroup]="grupo">
	<input type="email" formControlName="correo">
	<button (click)="grabar()">Grabar</button>
</form>

En el TypeScript implementaremos el siguiente código.

  grupo: FormGroup;

  ngOnInit() {
    this.grupo = new FormGroup({
      correo: new FormControl(null, [
        Validators.required,
        Validators.email
      ])
    });
  }

Supongamos que además se necesita que el correo no sea gratuito, es decir que no sea un correo de Gmail, Hotmail, Outlook o Yahoo.

  grupo: FormGroup;

  correosProhibidos: Array<string> = [
        "gmail.com", "hotmail.com", "outlook.com", "yahoo.com"
  ];

  ngOnInit() {
    this.grupo = new FormGroup({
      correo: new FormControl(null, [
        Validators.required,
        Validators.email
      ])
    });
  }

Creemos un método llamado "excluirCorreos".

  ...
  correosProhibidos: Array<string> = [
        "gmail.com", "hotmail.com", "outlook.com", "yahoo.com"
  ];
  ...

  excluirCorreos(form: FormControl): { [s: string]: boolean } {
    if (form.value) {
      const partes: string[] = form.value.split("@");

      if (partes[1]) {
        if (this.correosProhibidos.indexOf(partes[1].toLowerCase()) > -1) {
          return { correoProhibido: true };
        }
      }
    }

    return null;
  }

Agregamos el método "excluirCorreos" como un validador.

  grupo: FormGroup;

  correosProhibidos: Array<string> = [
        "gmail.com", "hotmail.com", "outlook.com", "yahoo.com"
  ];

  ngOnInit() {
    this.grupo = new FormGroup({
      correo: new FormControl(null, [
        Validators.required,
        Validators.email,
        this.excluirCorreos.bind(this)
      ])
    });
  }

Supongamos que agregamos los campos "contraseña" y "confirmar contraseña"

<form [formGroup]="grupo">
	<input type="email" formControlName="correo">
	<input type="password" formControlName="contrasena">
	<input type="password" formControlName="confirmarContrasena">
	<button (click)="grabar()">Grabar</button>
</form>

Creamos una función "confirmaContraseña". 

function confirmarContrasena(control: AbstractControl) {
  if (!control.parent || !control) {
    return;
  }

  const contrasena = control.parent.get("contrasena");
  const confirmarContrasena = control.parent.get("confirmarContrasena");

  if (!contrasena || !confirmarContrasena) {
    return;
  }

  if (confirmarContrasena.value === "") {
    return;
  }

  if (contrasena.value !== confirmarContrasena.value) {
    return {
      contrasenasNoCoinciden: true
    };
  }
}

Noten que para agregar el validador, ahora la implementación en el FormControl es un poco diferente.

ngOnInit() {
    this.grupo = new FormGroup({
      correo: new FormControl(null, [
        Validators.required,
        Validators.email,
        this.excluirCorreos.bind(this)
      ]),
      contrasena: new FormControl(null, Validators.required),
      confirmarContrasena: new FormControl(null, [
        Validators.required,
        this.confirmarContrasena
      ])
    });
  }

9 Angular: Formularios

By Sergio Hidalgo

9 Angular: Formularios

Fomularios: FormsModule vs ReactiveFormsModule, ¿cómo usarlos? ¿cómo validar?

  • 706