Vamos a crear un formulario interactivo en Svelte que nos permita introducir datos para una persona que busca trabajo, como nombre, apellidos, tipo de trabajo y rango salarial. La idea es construir este formulario pensando en eventos personalizados, para practicar cómo crear, exponer y propagar eventos que modifiquen un estado global con los datos introducidos. También veremos cómo enviar datos hacia abajo para mantener todo sincronizado.
Para empezar, creamos un proyecto nuevo con la estructura actualizada de Svelte y eliminamos componentes que no vamos a usar, como el contador por defecto. Montamos un servidor de desarrollo para ir viendo los cambios en tiempo real. Lo primero es maquetar el formulario usando la etiqueta semántica <form>, que aunque no vayamos a enviar datos por HTTP, nos permite manejar eventos como submit de forma accesible y estándar.
Definimos una función llamada envío que se ejecutará al enviar el formulario. En esta función, por ahora, simplemente mostraremos un alert con el estado actual de los datos, que es un objeto llamado estado con propiedades como nombre, apellido, sector y un rango salarial con valores mínimos y máximos. Por ejemplo:
let estado = {
nombre: 'Pepita',
apellido: 'Flores',
sector: 'frontend',
salario: {
min: 25000,
max: 45000
}
};
function envío(event) {
event.preventDefault();
alert(JSON.stringify(estado));
}
Es importante usar event.preventDefault() para evitar que el formulario recargue la página al enviarse, permitiéndonos controlar el comportamiento con JavaScript.
Para los campos de texto, como nombre y apellido, usamos inputs vinculados con bind:value a las propiedades del estado. Además, por accesibilidad, cada input debe tener un label asociado con el atributo for que coincida con el id del input. Por ejemplo:
<p>
<label for="nombre">Nombre</label>
<input id="nombre" type="text" bind:value={estado.nombre} />
</p>
<p>
<label for="apellido">Apellido</label>
<input id="apellido" type="text" bind:value={estado.apellido} />
</p>
Sin embargo, repetir este patrón para cada campo puede ser tedioso y propenso a errores, especialmente al mantener sincronizados los atributos id y for. Por eso, creamos un componente reutilizable llamado Input.svelte que encapsula esta lógica y nos permite pasarle las propiedades necesarias.
En Input.svelte exportamos las propiedades identifier, label y value. Usamos identifier para asignar el id del input y el for del label, y value para el binding bidireccional con bind:value. El truco está en exportar value y usar bind:value sin asignarlo directamente, lo que permite que el componente soporte el binding desde el padre y propague los cambios hacia arriba automáticamente.
<script>
export let identifier;
export let label;
export let value;
</script>
<p>
<label for={identifier}>{label}</label>
<input id={identifier} type="text" bind:value />
</p>
Luego, en el componente principal importamos este Input y lo usamos para los campos de nombre y apellido, pasando el identifier, el label y enlazando el value con las propiedades del estado:
<script>
import Input from './Input.svelte';
let estado = {
nombre: 'Pepita',
apellido: 'Flores',
sector: 'frontend',
salario: {
min: 25000,
max: 45000
}
};
function envío(event) {
event.preventDefault();
alert(JSON.stringify(estado));
}
</script>
<form on:submit={envío}>
<Input identifier="nombre" label="Nombre" bind:value={estado.nombre} />
<Input identifier="apellido" label="Apellido" bind:value={estado.apellido} />
<p><input type="submit" value="Enviar" /></p>
</form>
Este enfoque nos permite reducir la duplicación de código y mantener la lógica de los inputs centralizada. Además, gracias al binding en cadena, cualquier cambio en los inputs se refleja inmediatamente en el estado global, y al enviar el formulario podemos acceder a los datos actualizados.
Así, hemos construido la base de un formulario accesible, semántico y reactivo en Svelte, con componentes reutilizables y manejo personalizado de eventos para controlar el envío sin recargar la página. En siguientes pasos podremos añadir los desplegables y sliders para completar la interfaz.