Lección 9: Formularios | Curso JavaScript

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:

Flujo formulario

  1. Reposo: El usuario aún no ha introducido información. Los campos están vacíos, vírgenes, esperando ser rellenos.
  2. Validación: Se incluye los datos. Los campos deben cumplir unas directrices. En caso contrario se muestra al usuario como debe corregirlo.
  3. Envío: Los campos cumple las necesidades descritas. Se envía al servidor o servicio (como un API).
  4. 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();
});

Email

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.

Atribución/Reconocimiento-NoComercial-SinDerivados 4.0 Internacional

¿Me invitas a un café? ☕

Puedes hacerlo usando el terminal.

ssh customer@andros.dev -p 5555

Comentarios

{{ comments.length }} comentarios

Nuevo comentario

Nueva replica  {{ formatEllipsisAuthor(replyComment.author) }}

Acepto la política de Protección de Datos.

Escribe el primer comentario