Formularios en Angular — Diferencias Template y Reactive Forms

Artículo detallado donde encontraremos sus diferencias

Anartz Mugika Ledo🤗
15 min readJan 14, 2021

Los formularios son una característica muy común que se usan en cualquier aplicación.

En esta guía, vais a aprender sobre las dos técnicas utilizadas por Angular para crear formularios: formularios basados ​​en plantillas (Template Forms)y formularios reactivos (Reactive Forms).

Aparte de ello, también veremos cómo podemos agregar validaciones usando ambos enfoques.

Vamos a comenzar un nuevo artículo, para ver la diferencia entre los tipos de formulario más utilizados en Angular.

Desde la versión 11 de Angular, los “Template Forms” han quedado obsoletos y la idea de este artículo es mostrar las diferencias principales y las características en común, para que podamos hacer una transición, lo menos dolorosa posible.

1.- Diferencias de alto nivel entre los dos tipos

A continuación podremos verde las diferencias más destacadas entre los dos tipos:

  • Los formularios basados ​​en plantillas utilizan el “FormsModule”, mientras que los formularios reactivos se basan en “ReactiveFormsModule”.
  • Los formularios basados ​​en plantillas son de naturaleza asincrónica, mientras que los formularios reactivos son en su mayoría sincrónicos.
  • En un enfoque basado en plantillas, la mayor parte de la lógica se basa en la plantilla, mientras que en el enfoque basado en reactivos, la lógica reside principalmente en el componente .

Ahora que ya conocemos las diferencias principales, vamos a comenzar creando un nuevo proyecto en Angular y al crearlo generamos un componente y luego actualizaremos nuestro código de formulario.

2.- Creando los componentes de los dos tipos

Ejecutamos los comandos del CLI para crear un componente para los formularios mediante plantillas y para formularios reactivos.

ng generate component template-forms
ng generate component reactive-forms

Una vez creados, tendremos de esta manera el proyecto:

Estado actual del proyecto con los dos componentes creados

No hay que olvidarse de importar los componentes y el módulo para los formularios Reactivos (ReactiveFormsModule) en el módulo correspondiente, en este caso sin aplicar LazyLoading lo hago en el propio app.module.ts para centrarnos en los formularios únicamente.

Componentes y Módulo importados para poder usarlos

Ahora que ya tenemos los componentes añadidos, vamos al index.html y añadimos la referencia del fichero CSS para la versión 4.5.3 de Bootstrap y así poder trabajar con los colores de validaciones y la estructura de los formularios.

Referencia del CSS de Bootstrap. Podemos encontrarla aquí

Una vez añadido los estilos, tenemos preparado todo lo básico para empezar a trabajar con un formulario de registro en Angular, que lo realizaré tanto con un formulario por template como con un formulario reactivo.

Lo que vamos a construir es un formulario con validaciones donde introduciremos / gestionaremos la siguiente información.

  • Nombre de la persona que se registra (Nombre de Pila).
  • Email de registro.
  • Password.
  • Repetir Password.

3.- Formulario por template

Comenzamos con el desarrollo de los formularios, empezamos por el formulario por template que en la actualidad, a partir de la versión 11 de Angular, está obsoleto y por es importante darle cierto protagonismo para que podáis ver las diferencias entre un tipo de formulario y el otro haciendo lo mismo, para que podáis hacer la migración a lo actual sin mucho sufrimiento.

3.1.- Componente template-forms.component.ts

Definimos una interface para ir teniendo “a raya” los valores que componen el formulario de registro y podamos hacerlo estableciendo unos campos, que tendremos que respetar en todo momento.

Interface para los campos del formulario

Añadimos el siguiente código base del componente teniendo la interface como parte de ese fichero.

Inicialización del contenido del componente con lo básico

3.2.- Trabajando en el apartado del template.

Ahora que ya tenemos los elementos que componen la parte inicial de un formulario por template, vamos a empezar a trabajar con el formulario por template, en el fichero template-forms.component.html, añadiendo los 4 campos uno a uno, con sus validaciones e iré desgranando uno a uno como funcionará para más detalle

Para crear el apartado del formulario en el template, debemos de añadir lo siguiente:

Apariencia de la estructura del formulario con formulario de template

Una vez añadido ese código, vamos a tener en cuenta un par de cosas.

  • (ngSubmit())=“submit()” : Evento que se ejecuta cuando pulsamos el botón de submit, que todavía no está creado.
  • #forma: es la referencia por template, donde tendremos la información de si ese formulario es válido o no, que servirá por ejemplo para habilitar el botón submit que usaremos para enviar los datos del formulario validado.
  • “ngForm”: Valor que se asignada a la referencia por template para usar esa clase que se encuentra dentro de FormsModule.

Ahora que ya tenemos claro cómo funciona la parte superior del formulario en este tipo, vamos a ir a por los campos de manera individual.

3.2.1.- Trabajando con el campo nombre

En este caso, vamos a introducir el nombre de pila del usuario que se quiere registrar. Será muy sencillo y su validación solo requiere que tengamos algo introducido y un mínimo de 5 caracteres, para que se de el OK a la variable por template #forma

Tenemos que tener en cuenta y hay que añadir como recomendación estos campos para un correcto funcionamiento.

3.2.2.- Repaso de las propiedades de un campo

Las propiedades que son recomendables usar son de las cuales type, #<nombre-variable-template>, el valor de ngModel y name son el mínimo exigido, las otras son opcionales pero necesarias para este ejemplo:

  • type: text | number | email = Determinamos el tipo de dato que es. En este caso al ser un nombre de pila, usaremos “text”.
  • #name = Esto es la variable por template relacionada al campo del formulario correspondiente al nombre de pila. Podemos hacer con un email añadiendo #email y así sucesivamente. Es un valor que tenemos que tener en cuenta para las validaciones, para saber si ese valor es válido o inválido, por lo que procurad poner un nombre acorde a lo que vamos a introducir. En este caso, al trabajar con un nombre, me parece correcto añadir el valor “name”. En el caso del correo electrónico será “email”, en la contraseña “password” y en la repetición de la contraseña “repeatPass” o algo similar.
  • [(ngModel)] = Donde se asigna el valor de la propiedad de la propiedad que hemos inicializado en el componente. Hemos creado una propiedad de tipo IRegisterForm aplicando lo de la interface donde habían 4 campos, uno de ellos correspondía al nombre y lo reflejamos así: “register.name”. Es fundamental respetar los nombre.
  • placeholder = Texto descriptivo que ponemos en el campo, no obligatorio pero sí recomendable.
  • name = Nombre del input. Casualmente es “name” en este caso, pero en el correo será “email”, en la contraseña será “password” y etc.
  • minlength = Longitud mínima que tenemos que introducir, para que #name en la propiedad invalid sea false. Si ponemos menos de 5 caracteres, name.invalid será true y querrá decir que es erroneo.
  • class-fontrol=“form-control” = Clase de Bootstrap que se usa para el control del formulario con las validaciones.
  • required = Para establecer que ese campo es obligatorio
  • [ngClass]=“{‘valid’…}” = Se determina por condición, si ese campo es válido o no, para que se aplique una clase CSS dinámicamente. Fijaros de la importancia de “name.valid” y “name.invalid”. Lo que corresponde a “name” es lo que hemos definido como variable por template. Si hubiesemos llamado firstName, sería “firstName.valid” independientemente de lo que añadamos en el [(ngModel)] que es lo que coge de la propiedad del componente.

3.2.3.- Trabajando con los errores dados

Ahora que ya hemos analizado las propiedades del campo donde vamos a trabajar con la información del nombre de pila del usuario que se va a registrar, vamos a añadir un apartado para mostrar los mensajes de error, en el caso de que no cumpla unos requerimientos como introducir algo de contenido y más de 5 caracteres (minlength=5)

Parte del código que se mostrará, en el caso de que no se valide. 1) No tiene + de 4 caracteres. 2) No hay texto.

Este trozo del código se activa siempre y cuando “name” (del valor de la variable de template que sería #name) detecta que es inválido, por no cumplir unos requerimientos mínimos que hemos establecido, como que es obligatorio (required) y tenemos que añadir un mínimo de 5 caracteres (minlength = 5).

Y con esto, ya hemos destripado lo relacionado al nombre de pila y cómo funciona.

Los otros campos, básicamente funcionan igual, lo único que cambiará en algunos casos es el tipo de dato:

  • Correo electrónico: type = “email”.
  • Password y Repetir Password: type = “password”

En el caso del correo electrónico, para hacer la validación más exacta, añadimos una expresión regular mediante la propiedad “pattern” en el que establecemos las normas del contenido que debe de respetar. Si no lo respeta, da el valor del correo electrónico como “invalid” y saldrá un mensaje de error asociado a ese desajuste.

La expresión regular la podemos generar si tenemos conocimientos sobre ello o podemos usar un generador de expresiones regulares online como Regex 101.

3.2.4.- Resultado de lo trabajado

El código quedará de la siguiente manera en el fichero del template.

Código completo del template del formulario

3.2.5.- Botón de envío del formulario

Ahora, lo que tenemos que analizar es el apartado del botón de envío de la información.

Al final de todos los campos, tenemos el elemento “button” de tipo “submit” y dentro de él tenemos la directiva [disabled] que hará que esté / no esté disponible, dependiendo de si el formulario es válido.

Si os fijáis, dentro de ese valor tenemos “!forma.valid” que querrá decir, que mientras el formulario NO ES válido, se activa la directiva “[disabled]” y eso hace que el botón de envío no esté disponible.

Mirando en la app de Angular, tenemos los casos del formulario que no es válido al completo que hace que el botón de enviar la información no esté disponible, y como hemos comentado anteriormente, cuando se trabajaba con este tipo de formularios, las validaciones se hacía más enfocadas en el template, antes que hacerlas en el componente.

En la situación anterior no era válido, pero en esta ya cumple todos los requisitos mínimos exigibles para poder enviar la información.

Recordad, que antes hemos especificado que si enviamos la información, lo haríamos a la función “submit()” del componente (fijaros en la cabecera, en este código):

Evento ngSubmit que se activa al dar al botón de tipo “submit”

Esto lo que hará es llamar al método del componente template-forms.component.ts

Reflejado donde va a entrar para coger la información de lo del formulario

3.2.6.- Trabajando en el componente con los datos recibidos

Ahora que ya conseguimos comunicarnos con el método tenemos que añadir el código necesario para gestionar esa información y esta sería una pequeña propuesta, donde nos faltaría tener una llamada a un servicio de una API que tengamos disponible. Eso ya es cuestión de trabajo personal.

Código de la implementación de la función submit(), después de recibir los datos validados

Si ejecutamos con este código con esta apariencia el formulario, esto es lo que se verá en la consola.

Imagen final con el formulario con los datos introducidos y los obtenidos.

Llegados a este punto ya tenemos nuestro formulario validado y con la información disponible para poder enviarlo a un Backend propio o existente ajeno a nosotros. Ahora pasamos a trabajar con el mismo ejemplo pero trabajando con formularios reactivos, donde vamos a trabajar más enfocados en el componente.

4- Formulario Reactivos

Una vez que hemos trabajado en los formularios controlados por plantillas donde la mayor parte de la interacción se producía en la plantilla, en este caso,en los formularios reactivos, la mayor parte de la interacción se produce en el componente.

Vamos a trabajar con el mismo ejemplo base anterior, con los datos de registro de un usuario.

4.1.- Inicializando propiedades en el componente

En este caso, vamos a empezar a trabajar en el componente, que es donde más lógica vamos a aplicar, donde haremos todo el trabajo sucio del flujo de datos, para validarlos y tenerlos disponibles para trabajar con ello.

Comenzamos añadiendo las propiedades e inicializando los campos del formulario

En la primera imagen, vemos dos bloques principales.

En el primer apartado, será las inicializaciones principales de lo que necesitamos para empezar a trabajar.

  • Aquí se añade la propiedad registerForm: FormGroup, es es donde tendremos todas las propiedades de todos los campos que van a componer ese formulario como aspectos de si son obligatorios, longitud mínima, tipo de campo,…
  • Añadimos una propiedad submitted para hacer un control de si se ha pulsado o no el botón de enviar, para gestionar esta información.
  • Finalmente, en el constructor inyectamos la clase FormBuilder, que será la encargada de construir agrupando un formulario con sus datos por defecto, si son obligatorios, validaciones y otras opciones que vemos a continuación. Una vez construido todo, lo asignamos en FormGroup que hemos definido en registerForm, para añadirlo en el template de este componente.

En este segundo apartado, ya más centrados en el agrupamiento de los campos, con sus configuraciones individuales como valor por defecto, si es requerido, tipo de dato,… vamos desgranando como funciona.

Dentro del FormBuilder, llamando a la función “group” si ponemos el cursor sobre ello, nos aparece la siguiente información que nos describe que añadiremos en cada apartado.

Para más información de lo que se va a hacer, me voy a basar en estos dos puntos de la documentación oficial:

Teniendo en el siguiente código:

Creando un formulario con controles y dinámico

En la primera parte, tenemos los campos que compondrán el formulario, añadiendo los diferentes nombres como:

  • name
  • email
  • password
  • repeatPass

Luego para cada campo tendremos un apartado donde iremos añadiendo las opciones básicas. En el primer punto, donde en todos casos tenemos “” es el valor por defecto de ese campo y luego en la segunda opción se añade mediante Validators, una validación y en el caso de tener que cumplir dos validaciones (email y password) lo hacemos dentro de una lista.

Una vez que ya tenemos establecidas estas configuraciones, estamos haciendo prácticamente lo mismo que hacíamos en el ejemplo de los formularios por template, donde añadimos la información de la variable por template precedida por “#” y otros ajustes como required, minlength,…

En el segundo apartado, añadimos otras opciones como validaciones customizadas (personalizadas), como comprobar que las dos contraseñas son iguales u otras operaciones que necesitemos realizar.

En este ejemplo, añadimos la propiedad “validator” y dentro de ella añadimos una función MustMatch donde vamos a comprobar que los campos “password” y “repeatPass” SON COMPLETAMENTE IGUALES.

4.2.- Función “MustMatch” para comprobar contraseñas

En este caso, vamos a hacer uso de una función personalizada donde vamos a comprobar mediante un control, si “password y “repeatPass” SON IGUALES y en el caso de que no lo sean, nos devolverá un error que veremos a continuación.

Función donde comprobamos si son iguales las contraseñas

Pasamos dos argumentos a la función y recibimos los parámetros, con los nombres de las propiedades, que son “password” y “repeatPass” como he mencionado antes.

Creamos dos constantes, control (la que estará unida a “password”) y matchingControl (unida a “repeatPass”).

Este será el primer paso. Antes de hacer una comprobación, comprobamos si “matchingControl” tiene algún error de antes de una validación que fuese inválida anteriormente. Si lo es, ejecuta un return, y se mantiene con el error.

Si todavía no tiene ningún error, llegamos a este fragmento del código:

Comprobamos si las contraseñas son iguales

En este caso tenemos el condicional inicial que pregunta si son diferentes y en el caso de cumplir esta condición, asignamos en la función setErrors el objeto con la información mustMatch que usaremos posteriormente para mostrar los mensajes en el template como en este caso (lo veremos luego más detallado):

Imagen de la situación cuando contraseñas son diferentes.

4.3.- Añadiendo las funciones necesarias para trabajar en el template

Para terminar con el componente añadimos 3 funciones más que explicamos ahora mismo.

Funciones complementarias a las de inicio en un formulario Reactivo
  • get form(): La usamos para simplificar la propiedad del formulario de registro cuando vayamos a usar en el control de los diferentes campos. Así conseguimos escribir en el template, en vez de registerForm.controls, escribiendo form, estaremos haciendo lo mismo.
  • onSubmit(): Función donde se controla si es inválido el formulario y en el caso de ser VÁLIDO, seguir hacia delante para enviar los datos a nuestro servidor o API relacionado.
  • onReset(): Resetear el formulario, como si hiciesemos F5.

4.4.- Template del formulario Reactivo — reactive-forms.component.html

Ahora nos dirigimos al fichero y la estructura base de un formulario para empezar a construir la base visual de nuestro formulario.

Estructura inicial del formulario, antes de añadir los campos

4.4.1.- Selector “form”

Añadimos como hemos hecho anteriormente el selector “form”, pero a diferencia del formulario por plantilla, en vez de añadir la #<nombre-de-variable-por-template> para la validación, hacemos uso del binding de la propiedad para asignar directamente al formGroup (sin {{ }}) el valor de Angular, que en este caso es registerForm, dato que es de tipo FormGroup.

Tal y como hemos hecho en el formulario anterior, usa el evento ngSubmit que funcionará igual, en este caso llamando a la función onSubmit() que se puede ver en este caso.

4.4.2.- Botones de registro / cancelar

Para finalizar, se ha añadido un bloque con dos botones, para “Registrarse”, que será para enviar los datos que estará deshabilitado mientras “registerForm.invalid” sea igual a true. Esto será prácticamente igual que el anterior ejemplo.

Junto con el botón de registro, tenemos uno para cancelar, que limpiará todo el formulario, como si hiciesemos F5

4.4.3.- Añadiendo el primer campo — name

Comenzamos añadiendo nuestro primer campo, que es el que corresponde al nombre de pila del usuario que se va a registrar.

Tendremos algo así, en dos bloques principales que vamos a desgranar de manera detallada, para entender cómo funciona un campo único, para poder hacer lo mismo con los demás campos.

Campo “name” para añadir el nombre de pila.

4.4.3.1.- Elementos del input

En el input tenemos varios elementos que tenemos que analizar. Todos los elementos excepto formControlName los conocemos de usarlos en el formulario por template.

  • type: Puede ser un campo de texto (text), de tipo email,…
  • formControlName: Nombre que hemos asignado en el componente, dentro de los campos. Importante escribirlo igual, respetando minúsculas y mayúsculas.
formControlName = “name”
  • class: Usamos la “form-control” para aplicar los controles de formulario ya que estamos trabajando con Bootstrap.
  • [ngClass]: Se asigna clase válida o inválida, dependiendo de si es válido o no ese campo.

4.4.3.2.- Feedback de los errores

En este caso, se muestra los errores en el caso de no ser correcto el valor. En este caso particular comprueba si hay algo información en el momento de usar “Validator.required” y esa es la única condición que debe de cumplir.

Por ejemplo, en el caso de “email” (formControlName) tiene que cumplir que se ha escrito algo y que sea de tipo email, si no, nos dará un error, como se ve aquí a continuación.

Campo “email” con su feedback, en el caso de que haya errores.

4.4.4.- Código del apartado del HTML

Teniendo en cuenta todo esto, dejaremos el código en el apartado del HTML de la siguiente manera:

4.4.5.- Haciendo pruebas y viendo el Feedback

Realizaremos la prueba con valores incorrectos, para ver su estado y con la información correcta. Recordad que mientras el formulario NO ES VÁLIDO el botón de registro no se habilitará.

4.4.5.1.- Con errores

Al iniciar, al no introducir los valores requeridos mínimos, ya tenemos el feedback y con ello, no podremos pulsar “Registrarse”.

Formulario con errores y botón de registro deshabilitado

4.4.5.2.- Información correcta

En este caso introducimos todos los datos validados y con ello tendremos opción de Registrarnos. En ese momento que hacemos click, accede al componente y como no tiene ningún error, accede a la opción de mostrar un alert con la información del registro, que podríamos enviar a una API, que como os he comentado, tendríais que tener disponible.

Código del método onSubmit()

Muestra alerta con los datos correctos

Estado del formulario con la información correcta que se muestra en el alert.

Enviado datos del registro

5.- Conclusión.

Llegados a este punto, hemos podido ver el mismo ejemplo aplicado de las dos maneras disponibles.

Seguramente tengáis una más de vuestro gusto, pero os invito a que probéis la otra forma.

En el caso de estar trabajando con formularios por template, si váis a trabajar a partir de la 11, os recomiendo que vayáis “migrando” al uso de formularios reactivos.

Si seguis trabajando en versiones anteriores, os recomiendo usar la que más os resulte cómodo.

6.- Proyecto y código

Os dejo a continuación el enlace al proyecto en Stackblitz donde veréis todo el código, como está estructurado y lo mejor, podréis probarlo e incluso descargarlo.

Gracias y espero que sea de vuestra utilidad este artículo.

7.- Invitame a un café

Si este contenido o cualquier artículo de los que puedes encontrar aquí te resulta útil, si queréis ir obteniendo artículos de calidad, me gustaría vuestro apoyo.

Podéis apoyarme desde aquí:

8.- Presencia en redes sociales.

Podéis encontrarme en las siguientes redes.

9.- Cursos Online

--

--

Anartz Mugika Ledo🤗

[{#frontend:[#mobile:{#android, #kotlin, #ionic}}, {#web:{#angular, #qwik, #bootstrap}}],{#backend: [{#graphql, #nestjs,#express, #mongodb, #mysql}]}]