Lección 9: Formularios
Los formularios son una vía de entrada a la base de datos y, por ende, a la zona más sensible de la aplicación web. La información que recibiremos debe pasar un exhaustivo control de calidad: La edad será un número, no una dirección de correo; una foto no debe ser un archivo comprimido zip. Y así podríamos continuar con una larga lista de especificaciones que no perdonaremos al visitante ni en un solo acento.
No buscamos castigar, sino guiar*. Con mensajes claros, bien explicados, indicaremos la mejor forma de rellenar cada campo con un formato adecuado. Y hasta que no se cumplan los requisitos, ignoraremos cualquier información que nos llegue a la base de datos. Incluso evitaremos desde el frontend que puedan pulsar el botón para enviar. Somos los últimos responsables de todo lo que pueda suceder.
Validar desde el frontend (JavaScript) no evita que pueda llegar la información al servidor en el formato adecuado. Siempre debemos comprobar desde el backend y devolver un código 400 en caso contrario.
No obstante debes ser consciente de que un formulario pasará por 4 estados:
- Reposo: El usuario aún no ha introducido información. Los campos están vacíos, vírgenes, esperando ser rellenos.
- Validación: Se incluye los datos. Los campos deben cumplir unas directrices. En caso contrario se muestra al usuario como debe corregirlo.
- Envío: Los campos cumple las necesidades descritas. Se envía al servidor o servicio (como un API).
- Respuesta del servidor: Se informa al usuario de si ha llegado correctamente o ha fallado.
En esta lección nos enfocaremos en la validación con el objetivo de mejorar la experiencia del usuario. Veamos algunos ejemplos utilizados de forma recurrente.
Si quisieramos enviar el formulario de forma asíncrona puedes ojear la lección de AJAX.
Input o Textarea
Obligatorio
Tan sencillo como añadir el atributo required
en un input
.
<form>
<label for="name">Nombre: </label>
<input type="text" name="name" id="name" required autocomplete="off">
<button>Enviar</button>
</form>
Para personalizar el mensaje, como quitarlo cuando esté relleno, deberemos jugar con los eventos input
(escribe dentro del campo) como invalid
(no se cumple la validación del campo) que usaremos para mostrar el mensaje deseado con setCustomValidity()
.
const miInput = document.querySelector('input');
// Quita la validación mientras escribes
miInput.addEventListener('input', () => {
// Quita el mensaje según escribes
miInput.setCustomValidity('');
// Comprueba si debe validarlo
miInput.checkValidity();
});
// Muestra el mensaje de validación
miInput.addEventListener('invalid', () => {
miInput.setCustomValidity('Si no es molesta... ¿me dices tu nombre?');
});
Es recomendable usar autocomplete="off"
para evitar que el autocompletador de los navegadores tapen las validaciones.
Límite de carácteres
Incluimos el atributo pattern
en el input
a validar, con una secuencia de patrón regular.
<form>
<label>
<input type="text" id="marca" required pattern="^[a-zA-Z0-9]{4,}$" autocomplete="off">
</label>
<input type="submit" id="submit" value="Enviar">
</form>
Mientras que JavaScript sería igual al ejemplo anterior.
// Variables
const inputMarca = document.querySelector('#marca');
const mensajeErrorMarcaCorto = "Muy corta. Dame un nombre con 4 o mas caracteres.";
// Eventos
inputMarca.addEventListener('input', () => {
// Quita el mensaje según escribes
inputMarca.setCustomValidity('');
// Comprueba si debe validarlo
inputMarca.checkValidity();
});
inputMarca.addEventListener('invalid', () => {
inputMarca.setCustomValidity(mensajeErrorMarcaCorto);
});
Carácteres válidos
En este caso comprobaremos 2 casos:
- Debe estar relleno.
- Los carácteres permitidos son alfanuméricos, incluyendo espacios.
Incluimos el atributo pattern
en el input
a validar, con una secuencia de patrón regular.
<form>
<label for="name">Nombre: </label>
<input type="text" name="name" id="name" required pattern="^[a-zA-Z0-9 ]*$" autocomplete="off">
<button>Enviar</button>
</form>
const miInput = document.querySelector('input');
// Quita la validación mientras escribes
miInput.addEventListener('input', () => {
// Quita el mensaje según escribes
miInput.setCustomValidity('');
// Comprueba si debe validarlo
miInput.checkValidity();
});
// Muestra el mensaje de validación
miInput.addEventListener('invalid', () => {
if(miInput.value === '') {
// Campo vacío
miInput.setCustomValidity('Si no es molesta... ¿me dices tu nombre?');
} else {
// Patrón
miInput.setCustomValidity('Debes introducir carácteres alfanuméricos');
}
});
Quitar espacios innecesarios
Para evitar que nos envíen campos con espacios al principio y al final de un texto podemos ayudarnos de la función trim()
.
trim(' Time After time ')
//'Time After time'
Si lo usamos con el evento cuando se pierdo el foto (blur
) podremos corregirlo rápidamente.
// Quita los espacios al principio y al final
miInput.addEventListener('blur', () => {
miInput.value = miInput.value.trim();
});
Disponemos de un input de tipo email
que nos evitará usar complejos patrones.
<input type="email" required>
<form>
<label for="email">Email: </label>
<input type="email" name="email" id="email" required autocomplete="off">
<button>Enviar</button>
</form>
const miInput = document.querySelector('input');
// Quita la validación mientras escribes
miInput.addEventListener('input', () => {
// Quita el mensaje según escribes
miInput.setCustomValidity('');
// Comprueba si debe validarlo
miInput.checkValidity();
});
// Muestra el mensaje de validación
miInput.addEventListener('invalid', () => {
miInput.setCustomValidity('Me parece que esto no es un E-mail');
});
Números
En esta ocasión usamos el tipo number
.
<input type="number" required>
<form>
<label for="email">Email: </label>
<input type="email" name="email" id="email" required autocomplete="off">
<button>Enviar</button>
</form>
const miInput = document.querySelector('input');
// Quita la validación mientras escribes
miInput.addEventListener('input', () => {
// Quita el mensaje según escribes
miInput.setCustomValidity('');
// Comprueba si debe validarlo
miInput.checkValidity();
});
// Muestra el mensaje de validación
miInput.addEventListener('invalid', () => {
miInput.setCustomValidity('No es un número');
});
Checkbox
Es bastante común obligar a que un usuario acepte un checkbox antes de dejarle continuar (Llámalo acuerdo u extorsión legal). Sea el caso que sea, no difiere mucho de lo visto en anterioridad. Haremos uso el atributo required
.
<form>
<label>
<input type="checkbox" required autocomplete="off"> Acepto vender mi alma
</label>
<button>Enviar</button>
</form>
const miInput = document.querySelector('input');
// Quita la validación mientras escribes
miInput.addEventListener('input', () => {
// Quita el mensaje según escribes
miInput.setCustomValidity('');
// Comprueba si debe validarlo
miInput.checkValidity();
});
// Muestra el mensaje de validación
miInput.addEventListener('invalid', () => {
miInput.setCustomValidity('Si no aceptas no puedes continuar');
});
File
Veamos un ejemplo clásico, validar que un campo sea una imagen.
<input type="file" id="foto">
En el siguiente ejemplo validaremos que el campo de archivo, o tipo file
, cumpla los siguientes requisitos:
- Extensiones: jpg, jpeg y png.
- No supere los 2 Mb.
const oneMegaBytesInBytes = 10 ** 6;
const pesoLimite = oneMegaBytesInBytes * 2; // 2 megabyte
const extensionesPermitidas = ['jpg','jpeg','png'];
const miInput = document.querySelector('#foto');
function validarImagen () {
// Resetea mensaje
miInput.setCustomValidity('');
// Destructuramos para obtener el nombre y el tamaño
const { name: archivoNombre, size: archivoPeso } = this.files[0];
// Obtenemos la extensión
const fileExtension = archivoNombre.split(".").pop();
// Validamos si tienes una extensión válida
if (!extensionesPermitidas.includes(fileExtension)){
miInput.setCustomValidity('Formato no válido, solo se admite jpg y png');
}
// Validamos el peso
if(archivoPeso > pesoLimite) {
miInput.setCustomValidity('Demasiado grande');
}
}
miInput.addEventListener("input", validarImagen);
Envío
Si todos los campos son correctos se enviará, no obstante si existe la necesidad de enviarlo sin pulsar el botón haremos uso de la función submit()
de DOM formulario
<form id="mi-formulario">
<input type="text" value="">
</form>
const miFormulario = document.querySelector('#mi-formulario');
miFormulario.submit();
Ejemplo sin usar validaciones nativas
Partiendo de un código donde todos sus campos son de tipo texto, haremos validaciones usando únicamente las herramientas de JavaScript. Se le podría denominar la forma “vieja”.
<form id="formulario">
<p>
<label>
Nombre
<input type="text" id="nombre" value="" autocomplete="off">
</label>
</p>
<p>
<label>
Edad
<input id="edad" type="text" value="" autocomplete="off">
</label>
</p>
<p>
<label>
Mensaje
<textarea cols="30" id="mensaje" name="" rows="10" autocomplete="off"></textarea>
</label>
</p>
<p>
<input type="submit" id="enviar" value="Enviar"/>
</p>
</form>
<!-- Mensajes de error -->
<ul id="errores"></ul>
Vamos a validar sus campos sin depender de las validaciones automáticas que nos proporciona HTML.
// Variables
const formulario = document.querySelector('#formulario');
const nombre = document.querySelector('#nombre');
const edad = document.querySelector('#edad');
const mensaje = document.querySelector('#mensaje');
const enviar = document.querySelector('#enviar');
const errores = document.querySelector('#errores');
let mensajesErrores = [];
// Funciones
function validar (evento) {
// Evitar que se envie el formulario
evento.preventDefault();
// Vacia los mensajesErrores antes de rellenarlo nuevamente
mensajesErrores = [];
// VALIDACIONES
// Nombre es obligatorio
if (nombre.value.trim().length === 0) {
mensajesErrores = mensajesErrores.concat('Nombre es un campo obligatorio');
}
// Nombre carácteres válidos
if (!/^[a-zA-Z0-9]*$/.exec(nombre.value.trim())) {
mensajesErrores = mensajesErrores.concat('Nombre no tiene carácteres válidos');
}
// Edad debe ser números
if (isNaN(edad.value.trim())) {
mensajesErrores = mensajesErrores.concat('La edad debe ser un número');
}
// Comprobamos que el mensaje tiene un mínimo de 10 carácteres
if (mensaje.value.trim().length < 10) {
mensajesErrores = mensajesErrores.concat('Mensaje demasiado corto');
}
// ENVIAR O MOSTRAR MENSAJES
if (mensajesErrores.length === 0) {
// Enviamos el formulario si no hay errores
formulario.submit();
} else {
// Muestro los errores
errores.textContent = '';
mensajesErrores.forEach(function (mensaje) {
const miLi = document.createElement('li');
miLi.textContent = mensaje;
errores.appendChild(miLi);
});
}
}
// Eventos
formulario.addEventListener('submit', validar);
// Inicio
Actividad 1
Crea un formulario para entrar en un lugar secreto llamado: FBI (Federación de Bajitos Irritables).
Añade los campos adecuados y valídalos.
- Nombre: Obligatorio, solo texto.
- Contraseña: Obligatoria.
Actividad 2
Crea un formulario para publicar un producto de segunda mano.
Añade los campos adecuados y valídalos.
- Nombre: Obligatorio, solo texto.
- E-mail: Obligatoria, formato correcto.
- Precio: Solo número.
- Aceptar políticas: Obligatorio.
Nivel pesadilla 👹
Añade otro campo: Imagen. Debe ser una imagen que no supere los 5Mb.
Esta obra está bajo una Licencia Creative Commons Atribución-NoComercial-SinDerivadas 4.0 Internacional.
¿Me invitas a un café? ☕
Puedes hacerlo usando el terminal.
ssh customer@andros.dev -p 5555
Comentarios
Nuevo comentario
Nueva replica {{ formatEllipsisAuthor(replyComment.author) }}
Escribe el primer comentario
{{ comments.length }} comentarios