Formularios


Introducción

Es muy común que cuando vas al doctor, al banco o a algún establecimiento donde soliciten tu información, te pregunten una serie de datos personales con el fin de nutrir la base de datos del lugar que estás visitando. Cada vez que nutren de información su base de datos lo hacen mediante formularios que ayudan a un usuario a enviar la información y almacenarla. En el frontend existen muchas formas de poder enviar información y, por supuesto, validarla antes de que sea almacenada, pero todo se realiza mediante el llenado de diferentes formularios porque facilitan las entradas de datos y mejoran la experiencia de usuario. En este tema aprenderás cómo utilizar los formularios de una forma eficiente para que apoyen al usuario a hacer más fácil la captura de la información. Desde luego, también para que envíen la información correcta y sea válida para la base de datos.


Explicación

Formularios

Hay dos formas de trabajar con formularios: la primera son los formularios por template, y los otros son los formularios reactivos en Angular. Se le conoce como formulario de template porque toda la lógica de programación se encuentra en el template HTML. A continuación, se mostrará un formulario por template para poder visualizar la diferencia entre los formularios reactivos.

<div>

<form (ngSubmit)="procesar()" #miFormulario="ngForm">

<div>

<label>Frutas</label>

<div>

<input

type="text"

[ngModel]="initForm.fruta"

name="fruta"

placeholder="Nombre de la fruta"

required

minlength="3"

/>

<span *ngIf="esFrutaValida()"> Los nombres de las frutas son de mínimo 3 letras </span>

</div>

</div><div>

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

</div>

</form>

</div>

Tabla 1. Implementación de formulario por template en archivo HTML.

import { Component, OnInit, ViewChild } from '@angular/core';

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

 

@Component({

    selector: 'app-frutas',

    templateUrl: './frutas.component.html',

})

export class FrutasComponent implements OnInit {

@ViewChild('miFormulario') miFormulario!: NgForm;

 

initForm = {

  fruta: 'fresa',

};

 

constructor() {}

 

ngOnInit(): void {}

 

esFrutaValida(): boolean {

return (

this.miFormulario?.controls.fruta?.invalid &&

this.miFormulario?.controls.fruta?.touched

);

 

procesar() {

  this.miFormulario.resetForm({

  fruta: '',

   });

  }

}

Tabla 2. Implementación de formulario por template en archivo TypeScript.

Como te habrás dado cuenta, la mayor parte de la lógica de programación está en el archivo HTML, ya que lo realizan mediante directivas y estos son recomendables cuando tienes que trabajar formularios sencillos y como máximo requieras dos formularios en una aplicación.

Formularios reactivos

Los formularios reactivos, a diferencia de los formularios de template, tienen la mayor parte de la lógica de programación en el archivo en TypeScript, por lo que tratan de dejar el template HTML lo más simple posible. Los formularios reactivos dan más control y es más fácil implementar validaciones que los formularios por template no lo pueden realizar o su simplicidad los hacen inseguros e inestables. En los formularios reactivos tienen como ventaja que tienen un modelo estructural, son síncronos y se pueden validar por funciones.

Para trabajar con los formularios reactivos, se debe cargar su respectivo módulo desde @angular/forms en el módulo en el que los utilizarás. Por ejemplo:

import { NgModule } from '@angular/core';

import { CommonModule } from '@angular/common';

import { ReactiveFormsModule } from '@angular/forms';

 

import { ReactiveRoutingModule } from './reactive-routing.module';

 

@NgModule({

 declarations: [],

 imports: [CommonModule, ReactiveFormsModule, ReactiveRoutingModule],

})

export class ReactiveModule {}

Tabla 3. Incorporación de ReactiveFormsModule a módulo de trabajo.

En los formularios reactivos se necesitan dos objetos: FormGroup y FormControl. Estos son para poder controlar los valores del formulario.

Figura 1. Objetos necesarios para funcionamiento de formularios reactivos.

Angular (s.f.-a) nos indica que una instancia de control de formulario proporciona un método setValue() que actualiza el valor del control de formulario y valida la estructura del valor proporcionado contra la estructura del control. Por ejemplo, cuando se recuperan datos de un formulario desde una API o un servicio de backend, se utiliza el método setValue() para actualizar el control con su nuevo valor, reemplazando el valor anterior por completo. Con lo anterior, te puedes dar cuenta que los formularios reactivos tienen sus propios métodos para cambiar el valor de un control desde el código en Typescript.

import { Component, OnInit } from '@angular/core';

import { FormControl } from '@angular/forms';

 

@Component({

 selector: 'app-form-controlexample',

 templateUrl: './form-controlexample.component.html',

 styles: [],

})

export class FormControlexampleComponent implements OnInit {

 mascota = new FormControl('');

 

 constructor() {}

 

 ngOnInit(): void {}

 

 cambiaaPerro() {

  this.mascota.setValue('perro');

 }

}

Tabla 4. Ejemplo de uso de FormControl. Archivo HTML.

<h1>Form Control Example</h1>

<label for="name">Mascota: </label>

<input id="name" type="text" [formControl]="mascota" />

<p>Value: {{ mascota.value }}</p>

<button (click)="cambiaaPerro()">Cambiar a perro</button>

Tabla 5. Ejemplo de uso de FormControl. Archivo TypeScript.

Por otra parte, Angular (s.f.-a) menciona que una instancia de FormGroup proporciona su valor de modelo como un objeto reducido a partir de los valores de cada control del grupo. Una instancia de FormGroup tiene las mismas propiedades (como valor y sin tocar) y métodos (como setValue()) que una instancia de control de formulario.

Pero para la creación manual de instancias de control de formularios, puede resultar repetitiva por lo que te debes auxiliar de FormBuilder, que es un servicio para crear instancias de controles y grupos de formularios. Angular (s.f.-a) define el servicio FormBuilder como un proveedor inyectable que se proporciona con el módulo de formularios reactivos. Esta dependencia se inyecta en el constructor como cualquier servicio.

También puedes incluir una validación del formulario a través la clase Validators que puedes importar desde el paquete @angular/forms para asegurarte que los datos que proporciona el usuario son correctas y completas de acuerdo con la definición que está establecida en la aplicación. Angular (s.f.-a) menciona que esta sección cubre la adición de un único validador a un control de formulario y la visualización del estado general del formulario.

Aplicando todo lo anterior, un ejemplo sería el que se muestra a continuación:

import { Component, OnInit } from '@angular/core';

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

 

@Component({

 selector: 'app-simple',

 templateUrl: './simple.component.html',

})

export class SimpleComponent implements OnInit {

 constructor(private fb: FormBuilder) {}

 

 formulario: FormGroup = this.fb.group({

  nombre: [, [Validators.required, Validators.minLength(3)]],

  edad: [, [Validators.required, Validators.min(18)]],

 });

 

 ngOnInit() {

  this.formulario.reset({

   nombre: '',

   edad: 0,

  });

 }

 

 validar(campo: string) {

  return (

   this.formulario.controls[campo].errors &&

   this.formulario.controls[campo].touched

  );

 }

 

 guardar() {

  if (this.formulario.invalid) {

   this.formulario.markAllAsTouched();

   return;

  }

  this.formulario.reset();

 }

}

Tabla 6. Ejemplo de uso de FormGroup, FormBuilder y Validator en archivo HTML.

<h2>Formulario reactivo simple</h2>

<hr />

<form (ngSubmit)="guardar()" [formGroup]="formulario">

 <!-- Inicio nombre -->

 <div>

   <label>Nombre</label>

   <input type="text" formControlName="nombre" />

   <span *ngIf="validar('nombre')"> Debe de ser de 3 letras </span>

 </div>

 <!-- Fin nombre -->

 <!-- Inicio edad -->

 <div>

   <label>Edad</label>

   <input type="number" formControlName="edad" />

   <span *ngIf="validar('edad')"> El Edad debe de ser 18 o mayor </span>

 </div>

 <!-- Fin edad -->

 <button type="submit">Guardar</button>

</form>

Tabla 7. Ejemplo de uso de FormGroup, FormBuilder y Validator en archivo TypeScript.

Igualmente se pueden crear formularios dinámicos, cuyos controles se crean en tiempo de ejecución. En lugar de usar FormGroup, se utilizará la clase FormArray y se maneja igual que cualquier array en TypeScript. Angular (s.f.-a) nos explica que al igual que con las instancias de grupos de formularios, puedes insertar y eliminar dinámicamente controles de las instancias de array de formularios, y el valor de la instancia de array de formularios y el estado de validación se calculan a partir de sus controles hijos. Sin embargo, no es necesario definir una clave para cada control por su nombre, por lo que es una gran opción si no conoces el número de valores hijos de antemano. A continuación, un ejemplo de su uso:

import { Component, OnInit } from '@angular/core';

import {

 FormArray,

 FormBuilder,

 FormControl,

 FormGroup,

 Validators,

} from '@angular/forms';

 

@Component({

 selector: 'app-dinamic',

 templateUrl: './dinamic.component.html',

 styles: [],

})

export class DinamicComponent implements OnInit {

 formulario: FormGroup = this.fb.group({

  favoritos: this.fb.array(

   [

    ['Metal Gear', Validators.required],

    ['Death Stranding', Validators.required],

   ],

   Validators.required

  ),

 });

 

 nuevoFavorito: FormControl = this.fb.control('', Validators.required);

 

 get favoritosArr() {

  return this.formulario.get('favoritos') as FormArray;

 }

 

 constructor(private fb: FormBuilder) {}

 ngOnInit(): void {

  throw new Error('Error');

 }

 

 agregarFavorito() {

  if (this.nuevoFavorito.invalid) {

   return;

  }

 

  this.favoritosArr.push(

   this.fb.control(this.nuevoFavorito.value, Validators.required)

  );

 

  this.nuevoFavorito.reset();

 }

 

 eliminar(i: number) {

  this.favoritosArr.removeAt(i);

 }

 

 guardar() {

  if (this.formulario.invalid) {

   this.formulario.markAllAsTouched();

   return;

  }

  console.log(this.formulario.value);

 }

}

Tabla 8. Ejemplo de uso de formularios dinámicos en archivo TypeScript.

<h1>Reactivos Dinámicos</h1>

<hr />

 

<form autocomplete="off" [formGroup]="formulario" (ngSubmit)="guardar()">

 <div>

  <label>Agregar</label>

  <div>

   <input

    [formControl]="nuevoFavorito"

    placeholder="Agregar favorito"

    (keyup.enter)="agregarFavorito()"

   />

 

   <button type="button" (click)="agregarFavorito()">Agregar</button>

  </div>

 </div>

 

 <div>

  <label>Favoritos</label>

  <div formArrayName="favoritos">

   <div *ngFor="let favorito of favoritosArr.controls; let i = index">

    <input [formControlName]="i" />

 

    <button type="button" (click)="eliminar(i)">Eliminar</button>

   </div>

  </div>

 </div>

 

 <div>

  <button type="button" (click)="guardar()">Guardar</button>

 </div>

</form>

Tabla 9. Ejemplo de uso de formularios dinámicos en archivo HTML.

Validación de formularios

Cuando trabajamos con formularios, es importante revisar la calidad de los datos que se envían a la base de datos y no sean datos inválidos o datos que no cumplen con el formato debido. Por ejemplo, si se solicita un email, se debería validar que fuese una cadena texto que al menos contenga una @ y un dominio. Para esto se requieren las validaciones y para un formulario reactivo, el que controla las validaciones, como lo hemos visto, es la clase del componente. Angular (s.f.-b) señala que, en lugar de añadir validadores a través de atributos en la plantilla, se añaden funciones de validación directamente al modelo de control del formulario en la clase componente. Angular entonces llama a estas funciones validadoras cada vez que el valor del control cambia y estas pueden ser sincrónicas o asincrónicas.

Figura 2. Tipos de funciones validadoras en Angular.

Por razones de rendimiento, Angular (s.f.-b) nos expone que solo ejecuta los validadores async si todos los validadores sync pasan. Cada uno debe completarse antes de que se establezcan los errores. Los validadores incorporados los necesitaste en el ejemplo de la tabla 5 y 7. En caso de que las funciones validadoras en Angular no te sean de utilidad, puedes definir tus propias funciones de validación con el uso de las expresiones regulares.

Recuerda que Mozilla (2021) define las expresiones regulares como patrones que se utilizan para hacer coincidir combinaciones de caracteres en cadenas. Un patrón de expresión regular se compone de caracteres simples, como:

/abc/

Tabla 10. Ejemplo de expresión regular simple.

O una combinación de caracteres simples y especiales, como:

/ab*c/ o /Capítulo (\d)\.\d*/

Tabla 11. Ejemplo de expresión regular con combinación de caracteres especiales.

Las expresiones regulares las requieres de la siguiente manera:

import { Component, OnInit } from '@angular/core';

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

 

@Component({

 selector: 'app-validations',

 templateUrl: './validations.component.html',

 styles: [],

})

export class ValidationsComponent implements OnInit {

 usernamePattern: string = '([a-zA-Z])+';

 emailPattern: string = '^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$';

 

 constructor(private fb: FormBuilder) {}

 

 ngOnInit(): void {

  throw new Error('Method not implemented.');

 }

 

 formulario: FormGroup = this.fb.group({

  username: [

   '',

   [Validators.required, Validators.pattern(this.usernamePattern)],

  ],

  email: ['', [Validators.required, Validators.pattern(this.emailPattern)]],

 });

 

 errorCampo(campo: string) {

  return (

   this.formulario.get(campo)?.invalid && this.formulario.get(campo)?.touched

  );

 }

 

 submitForm() {

  console.log(this.formulario.value);

  this.formulario.markAllAsTouched();

 }

}

Tabla 12. Ejemplo de validación de formularios con expresiones regulares en archivo TypeScript.

<h2>Validaciones Reactivas</h2>

<hr />

 

<form [formGroup]="formulario" (ngSubmit)="submitForm()">

 <div>

  <label>Username</label>

  <input

   type="text"

   formControlName="username"

   placeholder="Username (solo letras)"

  />

  <span *ngIf="errorCampo('username')" style="color: red"

   >El formato de nombre no es correcto</span

  >

 </div>

 

 <div>

  <label>Email</label>

  <input

   type="email"

   formControlName="email"

   placeholder="Email del usuario"

  />

  <span *ngIf="errorCampo('email')" style="color: red"

   >El formato de email no es correcto</span

  >

 </div>

 

 <div>

  <button type="submit">Login</button>

 </div>

</form>

Tabla 13. Ejemplo de validación de formularios con expresiones regulares en archivo HTML.

 


Cierre

Los formularios son de gran importancia en una aplicación web, ya que alimentan la base de datos. La función principal de los formularios es obtener datos por parte del usuario para almacenarlos y luego procesarlos de acuerdo con la funcionalidad de una aplicación web. Como habrás visto, es elemental que el usuario proporcione los datos de acuerdo con el formato en el que los espera la aplicación, ya que, de no ser así, puede alimentar la base de datos con información “basura” que no contribuya al funcionamiento principal de la aplicación. Por ejemplo, no podemos esperar que se envíe un email de confirmación, si no se validó que este tuviera el formato correcto, por lo que se pondría en duda la funcionalidad de la aplicación. Es por lo que la validación se debe realizar para no comprometer el correcto funcionamiento de un sistema. ¿Cuáles podrían ser las consecuencias de no aplicar una validación correcta en un formulario? ¿Qué implicaciones en la base de datos podría tener que se llenaran los formularios de forma incorrecta?


Checkpoint

Asegúrate de:

  • Identificar la importancia de los formularios en una aplicación web para utilizarlos en un proyecto.
  • Implementar el tipo de formulario correcto, de acuerdo con tus necesidades para utilizarlos a una aplicación web.
  • Aplicar la validación de formularios de cualquier tipo en una aplicación web para comprobar la calidad de la información que introduce el usuario en un proyecto.

Bibliografía

  • Angular. (s.f.-a). Reactive forms. Recuperado de https://angular.io/guide/reactive-forms#reactive-forms
  • Angular. (s.f.-b). Validating form input. Recuperado de https://angular.io/guide/form-validation
  • Mozilla. (2021). Expresiones Regulares. Recuperado de https://developer.mozilla.org/es/docs/Web/JavaScript/Guide/Regular_Expressions

La obra presentada es propiedad de ENSEÑANZA E INVESTIGACIÓN SUPERIOR A.C. (UNIVERSIDAD TECMILENIO), protegida por la Ley Federal de Derecho de Autor; la alteración o deformación de una obra, así como su reproducción, exhibición o ejecución pública sin el consentimiento de su autor y titular de los derechos correspondientes es constitutivo de un delito tipificado en la Ley Federal de Derechos de Autor, así como en las Leyes Internacionales de Derecho de Autor.

El uso de imágenes, fragmentos de videos, fragmentos de eventos culturales, programas y demás material que sea objeto de protección de los derechos de autor, es exclusivamente para fines educativos e informativos, y cualquier uso distinto como el lucro, reproducción, edición o modificación, será perseguido y sancionado por UNIVERSIDAD TECMILENIO.

Queda prohibido copiar, reproducir, distribuir, publicar, transmitir, difundir, o en cualquier modo explotar cualquier parte de esta obra sin la autorización previa por escrito de UNIVERSIDAD TECMILENIO. Sin embargo, usted podrá bajar material a su computadora personal para uso exclusivamente personal o educacional y no comercial limitado a una copia por página. No se podrá remover o alterar de la copia ninguna leyenda de Derechos de Autor o la que manifieste la autoría del material.