ANG_FRONT

Práctica 14. Gestión de formularios en Angular: De plantillas a reactivos

Objetivo de la práctica

Duración aproximada

Instrucciones

  1. ¿Qué son los formularios basados en plantillas?

<form #miFormulario="ngForm" (ngSubmit)="onSubmit(miFormulario)">
  <label for="nombre">Nombre:</label>
  <input type="text" id="nombre" name="nombre" ngModel required>
  
  <button type="submit" [disabled]="miFormulario.invalid">Enviar</button>
</form>

En este ejemplo:

  1. Ventajas de los formularios basados en plantillas.
  1. Validaciones en formularios basados en plantillas

<input type="text" id="email" name="email" ngModel required email>
<div *ngIf="miFormulario.controls.email?.invalid && (miFormulario.controls.email?.touched || miFormulario.controls.email?.dirty)">
  <small *ngIf="miFormulario.controls.email?.errors?.required">El email es requerido.</small>
  <small *ngIf="miFormulario.controls.email?.errors?.email">El formato del email es inválido.</small>
</div>
  1. Manejo de eventos.

onSubmit(form: NgForm) {
  console.log('Formulario enviado:', form.value);
}
  1. Acceso a los valores del formulario.
  1. Buenas prácticas.

Formularios reactivos en Angular

  1. ¿Qué son los formularios reactivos?

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

@Component({
  selector: 'app-mi-formulario',
  template: `
    <form [formGroup]="miFormulario" (ngSubmit)="onSubmit()">
      <label for="nombre">Nombre:</label>
      <input id="nombre" formControlName="nombre">
      <div *ngIf="miFormulario.get('nombre').invalid && miFormulario.get('nombre').touched">
        <small *ngIf="miFormulario.get('nombre').errors.required">El nombre es requerido.</small>
      </div>

      <button type="submit" [disabled]="miFormulario.invalid">Enviar</button>
    </form>
  `
})
export class MiFormularioComponent {
  miFormulario: FormGroup;

  constructor(private fb: FormBuilder) {
    this.miFormulario = this.fb.group({
      nombre: ['', Validators.required]
    });
  }

  onSubmit() {
    console.log('Formulario enviado:', this.miFormulario.value);
  }
}
  1. Ventajas de los formularios reactivos.
  1. Estructura de un formulario reactivo.
  1. Validaciones en formularios reactivos

this.miFormulario = this.fb.group({
  nombre: ['', [Validators.required, Validators.minLength(3)]],
  email: ['', [Validators.required, Validators.email]]
});

  1. Manejo de eventos

this.miFormulario.get('nombre').valueChanges.subscribe(value => {
  console.log('Nombre cambiado:', value);
});

  1. Buenas prácticas

Validaciones de Form Input en Angular

  1. Tipos de validaciones.
  1. Validaciones en formularios basados en plantillas.

<form #miFormulario="ngForm" (ngSubmit)="onSubmit(miFormulario)">
  <label for="email">Email:</label>
  <input type="email" id="email" name="email" ngModel required email>
  <div *ngIf="miFormulario.controls.email?.invalid && (miFormulario.controls.email?.touched || miFormulario.controls.email?.dirty)">
    <small *ngIf="miFormulario.controls.email?.errors?.required">El email es requerido.</small>
    <small *ngIf="miFormulario.controls.email?.errors?.email">El formato del email es inválido.</small>
  </div>

  <button type="submit" [disabled]="miFormulario.invalid">Enviar</button>
</form>
  1. Validaciones en formularios reactivos.

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

@Component({
  selector: 'app-mi-formulario',
  template: `
    <form [formGroup]="miFormulario" (ngSubmit)="onSubmit()">
      <label for="nombre">Nombre:</label>
      <input id="nombre" formControlName="nombre">
      <div *ngIf="miFormulario.get('nombre').invalid && miFormulario.get('nombre').touched">
        <small *ngIf="miFormulario.get('nombre').errors.required">El nombre es requerido.</small>
        <small *ngIf="miFormulario.get('nombre').errors.minlength">El nombre debe tener al menos 3 caracteres.</small>
      </div>

      <button type="submit" [disabled]="miFormulario.invalid">Enviar</button>
    </form>
  `
})
export class MiFormularioComponent {
  miFormulario: FormGroup;

  constructor(private fb: FormBuilder) {
    this.miFormulario = this.fb.group({
      nombre: ['', [Validators.required, Validators.minLength(3)]]
    });
  }

  onSubmit() {
    console.log('Formulario enviado:', this.miFormulario.value);
  }
}
  1. Validaciones personalizadas.

import { AbstractControl, ValidationErrors } from '@angular/forms';

export function nombreValidator(control: AbstractControl): ValidationErrors | null {
  const valid = /^[A-Za-z]+$/.test(control.value);
  return valid ? null : { invalidNombre: true };
}

this.miFormulario = this.fb.group({
  nombre: ['', [Validators.required, nombreValidator]]
});

  1. Manejo de errores y mensajes.

<div *ngIf="miFormulario.get('nombre').invalid && miFormulario.get('nombre').touched">
  <small *ngIf="miFormulario.get('nombre').errors.required">El nombre es requerido.</small>
  <small *ngIf="miFormulario.get('nombre').errors.invalidNombre">El nombre solo puede contener letras.</small>
</div>
  1. Buenas prácticas.

Ejercicio Práctico: Creación de un formulario de registro en Angular

  1. Paso 1: Crear el Proyecto Angular.

npm install -g @angular/cli

ng new formulario-registro

cd formulario-registro

npm install @angular/forms
  1. Paso 2: Estructura del proyecto.

formulario-registro/
│
├── src/
│   ├── app/
│   │   ├── registro-reactivo/
│   │   │   ├── registro-reactivo.component.ts
│   │   │   ├── registro-reactivo.component.html
│   │   │   ├── registro-reactivo.component.css
│   │   │   └── registro-reactivo.component.spec.ts
│   │   │
│   │   ├── registro-plantilla/
│   │   │   ├── registro-plantilla.component.ts
│   │   │   ├── registro-plantilla.component.html
│   │   │   ├── registro-plantilla.component.css
│   │   │   └── registro-plantilla.component.spec.ts
│   │   │
│   │   ├── app.component.ts
│   │   ├── app.component.html
│   │   ├── app.module.ts
│   │   └── ...
│   └── ...
└── ...
  1. Paso 3: Crear componentes.

ng generate component registro-reactivo

ng generate component registro-plantilla
  1. Paso 4: Implementar el formulario reactivo.

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

@Component({
  selector: 'app-registro-reactivo',
  templateUrl: './registro-reactivo.component.html',
})
export class RegistroReactivoComponent {
  miFormulario: FormGroup;

  constructor(private fb: FormBuilder) {
    this.miFormulario = this.fb.group({
      nombre: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]],
      contrasena: ['', [Validators.required, Validators.minLength(6)]],
    });
  }

  onSubmit() {
    console.log('Formulario Reactivo enviado:', this.miFormulario.value);
  }
}


<form [formGroup]="miFormulario" (ngSubmit)="onSubmit()">
  <label for="nombre">Nombre:</label>
  <input id="nombre" formControlName="nombre">
  <div *ngIf="miFormulario.get('nombre').invalid && miFormulario.get('nombre').touched">
    <small>El nombre es requerido.</small>
  </div>

  <label for="email">Email:</label>
  <input id="email" formControlName="email">
  <div *ngIf="miFormulario.get('email').invalid && (miFormulario.get('email').touched || miFormulario.get('email').dirty)">
    <small *ngIf="miFormulario.get('email').errors?.required">El email es requerido.</small>
    <small *ngIf="miFormulario.get('email').errors?.email">El formato del email es inválido.</small>
  </div>

  <label for="contrasena">Contraseña:</label>
  <input id="contrasena" type="password" formControlName="contrasena">
  <div *ngIf="miFormulario.get('contrasena').invalid && miFormulario.get('contrasena').touched">
    <small *ngIf="miFormulario.get('contrasena').errors?.required">La contraseña es requerida.</small>
    <small *ngIf="miFormulario.get('contrasena').errors?.minlength">La contraseña debe tener al menos 6 caracteres.</small>
  </div>

  <button type="submit" [disabled]="miFormulario.invalid">Registrar</button>
</form>

  1. Paso 5: Implementar el formulario basado en plantillas.

import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-registro-plantilla',
  templateUrl: './registro-plantilla.component.html',
})
export class RegistroPlantillaComponent {
  onSubmit(form: NgForm) {
    console.log('Formulario de Plantilla enviado:', form.value);
  }
}

<form #miFormulario="ngForm" (ngSubmit)="onSubmit(miFormulario)">
  <label for="nombre">Nombre:</label>
  <input type="text" id="nombre" name="nombre" ngModel required>
  <div *ngIf="miFormulario.controls.nombre?.invalid && miFormulario.controls.nombre?.touched">
    <small>El nombre es requerido.</small>
  </div>

  <label for="email">Email:</label>
  <input type="email" id="email" name="email" ngModel required email>
  <div *ngIf="miFormulario.controls.email?.invalid && (miFormulario.controls.email?.touched || miFormulario.controls.email?.dirty)">
    <small *ngIf="miFormulario.controls.email?.errors?.required">El email es requerido.</small>
    <small *ngIf="miFormulario.controls.email?.errors?.email">El formato del email es inválido.</small>
  </div>

  <label for="contrasena">Contraseña:</label>
  <input type="password" id="contrasena" name="contrasena" ngModel required minlength="6">
  <div *ngIf="miFormulario.controls.contrasena?.invalid && miFormulario.controls.contrasena?.touched">
    <small *ngIf="miFormulario.controls.contrasena?.errors?.required">La contraseña es requerida.</small>
    <small *ngIf="miFormulario.controls.contrasena?.errors?.minlength">La contraseña debe tener al menos 6 caracteres.</small>
  </div>

  <button type="submit" [disabled]="miFormulario.invalid">Registrar</button>
</form>
  1. Paso 6: Modificar el módulo principal

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { FormsModule } from '@angular/forms'; // Importar para formularios basados en plantillas
import { AppComponent } from './app.component';
import { RegistroReactivoComponent } from './registro-reactivo/registro-reactivo.component';
import { RegistroPlantillaComponent } from './registro-plantilla/registro-plantilla.component';

@NgModule({
  declarations: [
    AppComponent,
    RegistroReactivoComponent,
    RegistroPlantillaComponent
  ],
  imports: [
    BrowserModule,
    ReactiveFormsModule,
    FormsModule // Añadir aquí
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

  1. Paso 7: Integrar en la aplicación.

<h1>Formulario de Registro</h1>

<h2>Registro con Formulario Reactivo</h2>
<app-registro-reactivo></app-registro-reactivo>

<h2>Registro con Formulario Basado en Plantillas</h2>
<app-registro-plantilla></app-registro-plantilla>

  1. Paso 8: Ejecutar la aplicación.

ng serve