Saltearse al contenido

Construye formularios HTML en páginas de Astro

En modo SSR, las páginas Astro pueden mostrar y manejar formularios. En esta receta, usarás un formulario HTML estándar para enviar datos al servidor. El script de tu frontmatter manejará los datos en el servidor, sin enviar JavaScript al cliente.

Prerrequisitos

  • Un proyecto con SSR (output: 'server') habilitado

Receta

  1. Crea o identifica una página .astro que contenga tu formulario y tu código de manejo. Por ejemplo, podrías agregar una página de registro:

    src/pages/register.astro
    ---
    ---
    <h1>Register</h1>
  2. Agrega una etiqueta <form> con algunas entradas a la página. Cada entrada debe tener un atributo name que describa el valor de esa entrada.

    Asegúrate de incluir un elemento <button> o <input type="submit"> para enviar el formulario.

    src/pages/register.astro
    ---
    ---
    <h1>Registro</h1>
    <form>
    <label>
    Usuario:
    <input type="text" name="username" />
    </label>
    <label>
    Correo electrónico:
    <input type="email" name="email" />
    </label>
    <label>
    Contraseña:
    <input type="password" name="password" />
    </label>
    <button>Enviar</button>
    </form>
  3. Usa atributos de validación para proveer validación básica del lado del cliente que funciona incluso si JavaScript está deshabilitado.

    En este ejemplo,

    • required previene el envío del formulario hasta que el campo esté lleno.
    • minlength establece una longitud mínima requerida para el texto de entrada.
    • type="email" también introduce validación que solo aceptará un formato de correo electrónico válido.
    src/pages/register.astro
    ---
    ---
    <h1>Registro</h1>
    <form>
    <label>
    Usuario:
    <input type="text" name="username" required />
    </label>
    <label>
    Correo electrónico:
    <input type="email" name="email" required />
    </label>
    <label>
    Contraseña:
    <input type="password" name="password" required minlength="6" />
    </label>
    <button>Enviar</button>
    </form>
  4. El envío del formulario causará que el navegador solicite la página nuevamente. Cambia el método de transferencia de datos del formulario a POST para enviar los datos del formulario como parte del cuerpo de la Request, en lugar de como parámetros de URL.

    src/pages/register.astro
    ---
    ---
    <h1>Registro</h1>
    <form method="POST">
    <label>
    Usuario:
    <input type="text" name="username" required />
    </label>
    <label>
    Correo electrónico:
    <input type="email" name="email" required />
    </label>
    <label>
    Contraseña:
    <input type="password" name="password" required minlength="6" />
    </label>
    <button>Enviar</button>
    </form>
  5. Comprueba el método POST en el frontmatter y accede a los datos del formulario usando Astro.request.formData(). Envuelve esto en un bloque try ... catch para manejar los casos en los que la solicitud POST no fue enviada por un formulario y el formData es inválido.

    src/pages/register.astro
    ---
    if (Astro.request.method === "POST") {
    try {
    const data = await Astro.request.formData();
    const name = data.get("username");
    const email = data.get("email");
    const password = data.get("password");
    // Do something with the data
    } catch (error) {
    if (error instanceof Error) {
    console.error(error.message);
    }
    }
    }
    ---
    <h1>Registro</h1>
    <form method="POST">
    <label>
    Usuario:
    <input type="text" name="username" required />
    </label>
    <label>
    Correo electrónico:
    <input type="email" name="email" required />
    </label>
    <label>
    Contraseña:
    <input type="password" name="password" required minlength="6" />
    </label>
    <button>Enviar</button>
    </form>
  6. Valida los datos del formulario en el servidor. Esto debería incluir la misma validación realizada en el cliente para evitar envíos maliciosos a tu endpoint y brindar soporte a los raros navegadores heredados que no tienen validación de formularios.

    También puedes incluir validación que no se puede hacer en el cliente. Por ejemplo, este ejemplo verifica si el correo electrónico ya está en la base de datos.

    Los mensajes de error pueden ser enviados al cliente almacenándolos en un objeto errors y accediendo a él en la plantilla.

    src/pages/register.astro
    ---
    import { isRegistered, registerUser } from "../../data/users"
    import { isValidEmail } from "../../utils/isValidEmail";
    const errors = { username: "", email: "", password: "" };
    if (Astro.request.method === "POST") {
    try {
    const data = await Astro.request.formData();
    const name = data.get("username");
    const email = data.get("email");
    const password = data.get("password");
    if (typeof name !== "string" || name.length < 1) {
    errors.username += "Please enter a username. ";
    }
    if (typeof email !== "string" || !isValidEmail(email)) {
    errors.email += "Email is not valid. ";
    } else if (await isRegistered(email)) {
    errors.email += "Email is already registered. ";
    }
    if (typeof password !== "string" || password.length < 6) {
    errors.password += "Password must be at least 6 characters. ";
    }
    const hasErrors = Object.values(errors).some(msg => msg)
    if (!hasErrors) {
    await registerUser({name, email, password});
    return Astro.redirect("/login");
    }
    } catch (error) {
    if (error instanceof Error) {
    console.error(error.message);
    }
    }
    }
    ---
    <h1>Registro</h1>
    <form method="POST">
    <label>
    Usuario:
    <input type="text" name="username" />
    </label>
    {errors.username && <p>{errors.username}</p>}
    <label>
    Correo electrónico:
    <input type="email" name="email" required />
    </label>
    {errors.email && <p>{errors.email}</p>}
    <label>
    Contraseña:
    <input type="password" name="password" required minlength="6" />
    </label>
    {errors.password && <p>{errors.password}</p>}
    <button>Registro</button>
    </form>