Formularios en Spring MVC

Intermedio
SpringBoot
SpringBoot
Actualizado: 12/06/2025

¡Desbloquea el curso de SpringBoot completo!

IA
Ejercicios
Certificado
Entrar

Mira la lección en vídeo

Accede al vídeo completo de esta lección y a más contenido exclusivo con el Plan Plus.

Desbloquear Plan Plus

Binding de formularios con objetos con Thymeleaf

El binding de formularios es el proceso mediante el cual Spring MVC conecta automáticamente los datos enviados desde un formulario HTML con los atributos de un objeto Java. Esta funcionalidad elimina la necesidad de extraer manualmente cada campo del formulario y asignarlo a las propiedades correspondientes del objeto.

Thymeleaf facilita enormemente este proceso proporcionando atributos especiales que establecen la conexión bidireccional entre el formulario y el objeto del modelo. Cuando el usuario envía el formulario, Spring MVC utiliza esta información para poblar automáticamente el objeto con los valores introducidos.

Configuración básica del objeto modelo

Para implementar el binding, necesitamos un objeto Java que represente los datos del formulario. Este objeto debe seguir las convenciones de JavaBean, con propiedades privadas y métodos getter y setter públicos:

public class Usuario {
    private String nombre;
    private String email;
    private Integer edad;
    
    // Constructor vacío requerido
    public Usuario() {}
    
    // Getters y setters
    public String getNombre() {
        return nombre;
    }
    
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
    
    public String getEmail() {
        return email;
    }
    
    public void setEmail(String email) {
        this.email = email;
    }
    
    public Integer getEdad() {
        return edad;
    }
    
    public void setEdad(Integer edad) {
        this.edad = edad;
    }
}

Preparación del controlador

El controlador debe proporcionar una instancia del objeto al modelo para que Thymeleaf pueda establecer el binding. Esto se hace tanto para mostrar el formulario como para procesarlo:

@Controller
public class UsuarioController {
    
    @GetMapping("/usuario/nuevo")
    public String mostrarFormulario(Model model) {
        // Crear una instancia vacía para el binding
        model.addAttribute("usuario", new Usuario());
        return "formulario-usuario";
    }
    
    @PostMapping("/usuario/guardar")
    public String procesarFormulario(@ModelAttribute Usuario usuario, Model model) {
        // El objeto usuario ya contiene los datos del formulario
        // gracias al binding automático
        model.addAttribute("mensaje", "Usuario guardado: " + usuario.getNombre());
        return "resultado";
    }
}

Implementación del formulario con Thymeleaf

En la plantilla Thymeleaf, utilizamos el atributo th:object para vincular el formulario con el objeto del modelo, y th:field para conectar cada campo individual:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Registro de Usuario</title>
</head>
<body>
    <h2>Nuevo Usuario</h2>
    
    <form th:action="@{/usuario/guardar}" th:object="${usuario}" method="post">
        
        <div>
            <label for="nombre">Nombre:</label>
            <input type="text" th:field="*{nombre}" id="nombre" />
        </div>
        
        <div>
            <label for="email">Email:</label>
            <input type="email" th:field="*{email}" id="email" />
        </div>
        
        <div>
            <label for="edad">Edad:</label>
            <input type="number" th:field="*{edad}" id="edad" />
        </div>
        
        <button type="submit">Guardar</button>
    </form>
</body>
</html>

El atributo th:field="*{nombre}" es equivalente a escribir name="nombre" y value="${usuario.nombre}", pero de forma más concisa y con funcionalidades adicionales como la gestión automática de errores.

Binding con objetos anidados

Cuando trabajamos con objetos más complejos que contienen propiedades anidadas, el binding sigue funcionando utilizando la notación de punto:

public class Persona {
    private String nombre;
    private Direccion direccion;
    
    // Constructor, getters y setters
    public Persona() {
        this.direccion = new Direccion(); // Inicializar objeto anidado
    }
    
    // Resto de métodos...
}

public class Direccion {
    private String calle;
    private String ciudad;
    private String codigoPostal;
    
    // Constructor vacío, getters y setters
}

En el formulario, accedemos a las propiedades anidadas usando la sintaxis de punto:

<form th:action="@{/persona/guardar}" th:object="${persona}" method="post">
    
    <div>
        <label for="nombre">Nombre:</label>
        <input type="text" th:field="*{nombre}" id="nombre" />
    </div>
    
    <div>
        <label for="calle">Calle:</label>
        <input type="text" th:field="*{direccion.calle}" id="calle" />
    </div>
    
    <div>
        <label for="ciudad">Ciudad:</label>
        <input type="text" th:field="*{direccion.ciudad}" id="ciudad" />
    </div>
    
    <div>
        <label for="codigoPostal">Código Postal:</label>
        <input type="text" th:field="*{direccion.codigoPostal}" id="codigoPostal" />
    </div>
    
    <button type="submit">Guardar</button>
</form>

Ventajas del binding automático

El binding de formularios con Thymeleaf ofrece múltiples beneficios prácticos:

  • Reducción de código: Elimina la necesidad de extraer manualmente cada parámetro del request
  • Mantenimiento simplificado: Los cambios en el objeto se reflejan automáticamente en el formulario
  • Gestión de tipos: Spring MVC convierte automáticamente los strings del formulario a los tipos apropiados
  • Reutilización: El mismo objeto puede usarse para mostrar y procesar el formulario

Esta aproximación hace que el desarrollo de formularios sea más eficiente y menos propenso a errores, permitiendo que el desarrollador se concentre en la lógica de negocio en lugar de en los detalles de manipulación de datos del formulario.

Tipos de campos en formularios y conversiones

Guarda tu progreso

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

Spring MVC proporciona soporte automático para diferentes tipos de campos en formularios HTML, manejando las conversiones necesarias entre los strings enviados por el navegador y los tipos de datos Java correspondientes. Esta funcionalidad permite trabajar con formularios complejos de manera transparente.

Campos de texto y conversiones básicas

Los campos de texto son los más comunes y Spring MVC maneja automáticamente las conversiones básicas entre strings y tipos primitivos:

public class Producto {
    private String nombre;
    private Double precio;
    private Integer stock;
    private Boolean disponible;
    
    // Constructor vacío y getters/setters
    public Producto() {}
    
    // Getters y setters...
}

En el formulario, estos campos se renderizan como inputs de texto, pero Spring MVC los convierte automáticamente:

<form th:action="@{/producto/guardar}" th:object="${producto}" method="post">
    
    <div>
        <label for="nombre">Nombre del producto:</label>
        <input type="text" th:field="*{nombre}" id="nombre" />
    </div>
    
    <div>
        <label for="precio">Precio:</label>
        <input type="number" step="0.01" th:field="*{precio}" id="precio" />
    </div>
    
    <div>
        <label for="stock">Stock:</label>
        <input type="number" th:field="*{stock}" id="stock" />
    </div>
    
    <button type="submit">Guardar</button>
</form>

Campos de selección y checkboxes

Los campos de selección permiten al usuario elegir entre opciones predefinidas. Thymeleaf facilita la creación de estos elementos con binding automático:

public class Usuario {
    private String nombre;
    private String pais;
    private Boolean suscrito;
    private List<String> intereses;
    
    public Usuario() {
        this.intereses = new ArrayList<>();
    }
    
    // Getters y setters...
}

Selectores desplegables con opciones estáticas:

<div>
    <label for="pais">País:</label>
    <select th:field="*{pais}" id="pais">
        <option value="">Seleccionar país</option>
        <option value="ES">España</option>
        <option value="FR">Francia</option>
        <option value="IT">Italia</option>
        <option value="PT">Portugal</option>
    </select>
</div>

Checkboxes individuales para valores booleanos:

<div>
    <input type="checkbox" th:field="*{suscrito}" id="suscrito" />
    <label for="suscrito">Suscribirse al boletín</label>
</div>

Checkboxes múltiples para listas de valores:

<div>
    <label>Intereses:</label>
    <input type="checkbox" th:field="*{intereses}" value="tecnologia" id="tech" />
    <label for="tech">Tecnología</label>
    
    <input type="checkbox" th:field="*{intereses}" value="deportes" id="sports" />
    <label for="sports">Deportes</label>
    
    <input type="checkbox" th:field="*{intereses}" value="musica" id="music" />
    <label for="music">Música</label>
</div>

Campos de fecha y tiempo

Los campos de fecha requieren conversiones especiales entre strings y objetos LocalDate o LocalDateTime. Spring MVC maneja estas conversiones automáticamente:

public class Evento {
    private String titulo;
    private LocalDate fecha;
    private LocalTime hora;
    private LocalDateTime fechaCreacion;
    
    // Constructor vacío y getters/setters
}

En el formulario, utilizamos los tipos de input HTML5 apropiados:

<form th:action="@{/evento/guardar}" th:object="${evento}" method="post">
    
    <div>
        <label for="titulo">Título:</label>
        <input type="text" th:field="*{titulo}" id="titulo" />
    </div>
    
    <div>
        <label for="fecha">Fecha:</label>
        <input type="date" th:field="*{fecha}" id="fecha" />
    </div>
    
    <div>
        <label for="hora">Hora:</label>
        <input type="time" th:field="*{hora}" id="hora" />
    </div>
    
    <div>
        <label for="fechaCreacion">Fecha y hora de creación:</label>
        <input type="datetime-local" th:field="*{fechaCreacion}" id="fechaCreacion" />
    </div>
    
    <button type="submit">Crear evento</button>
</form>

Selectores con datos dinámicos

Frecuentemente necesitamos poblar selectores con datos dinámicos provenientes de la base de datos. El controlador debe proporcionar estas opciones al modelo:

@Controller
public class ProductoController {
    
    @GetMapping("/producto/nuevo")
    public String mostrarFormulario(Model model) {
        model.addAttribute("producto", new Producto());
        
        // Proporcionar opciones dinámicas
        List<Categoria> categorias = categoriaService.obtenerTodas();
        model.addAttribute("categorias", categorias);
        
        return "formulario-producto";
    }
}

En la plantilla, iteramos sobre las opciones dinámicas:

<div>
    <label for="categoria">Categoría:</label>
    <select th:field="*{categoriaId}" id="categoria">
        <option value="">Seleccionar categoría</option>
        <option th:each="categoria : ${categorias}" 
                th:value="${categoria.id}" 
                th:text="${categoria.nombre}">
        </option>
    </select>
</div>

Radio buttons para selección única

Los radio buttons permiten seleccionar una única opción de un grupo. Son útiles cuando las opciones son pocas y queremos mostrarlas todas visibles:

public class Encuesta {
    private String nombre;
    private String satisfaccion;
    
    // Constructor vacío y getters/setters
}
<div>
    <label>Nivel de satisfacción:</label>
    
    <input type="radio" th:field="*{satisfaccion}" value="muy_bajo" id="muy_bajo" />
    <label for="muy_bajo">Muy bajo</label>
    
    <input type="radio" th:field="*{satisfaccion}" value="bajo" id="bajo" />
    <label for="bajo">Bajo</label>
    
    <input type="radio" th:field="*{satisfaccion}" value="medio" id="medio" />
    <label for="medio">Medio</label>
    
    <input type="radio" th:field="*{satisfaccion}" value="alto" id="alto" />
    <label for="alto">Alto</label>
    
    <input type="radio" th:field="*{satisfaccion}" value="muy_alto" id="muy_alto" />
    <label for="muy_alto">Muy alto</label>
</div>

Áreas de texto para contenido extenso

Para contenido más largo, utilizamos áreas de texto que también se integran perfectamente con el binding:

<div>
    <label for="descripcion">Descripción:</label>
    <textarea th:field="*{descripcion}" id="descripcion" rows="4" cols="50">
    </textarea>
</div>

Conversiones personalizadas

Cuando necesitamos conversiones más complejas, Spring MVC permite definir convertidores personalizados. Por ejemplo, para convertir strings a enums:

public enum EstadoPedido {
    PENDIENTE, PROCESANDO, ENVIADO, ENTREGADO
}

public class Pedido {
    private String numero;
    private EstadoPedido estado;
    
    // Constructor vacío y getters/setters
}

El enum se maneja automáticamente en el formulario:

<div>
    <label for="estado">Estado:</label>
    <select th:field="*{estado}" id="estado">
        <option th:each="estado : ${T(com.ejemplo.EstadoPedido).values()}" 
                th:value="${estado}" 
                th:text="${estado}">
        </option>
    </select>
</div>

Esta flexibilidad en los tipos de campos permite crear formularios ricos y funcionales que se adaptan a las necesidades específicas de cada aplicación, mientras Spring MVC se encarga automáticamente de las conversiones necesarias entre la representación HTML y los objetos Java.

Aprendizajes de esta lección de SpringBoot

  • Comprender el concepto de binding de formularios en Spring MVC y su integración con Thymeleaf.
  • Aprender a configurar objetos JavaBeans para el binding automático de datos.
  • Implementar controladores que gestionen formularios y procesen datos mediante @ModelAttribute.
  • Utilizar diferentes tipos de campos HTML (texto, selección, checkboxes, fechas, radio buttons, áreas de texto) con binding automático.
  • Manejar conversiones automáticas y personalizadas entre tipos de datos HTML y Java, incluyendo objetos anidados y enums.

Completa este curso de SpringBoot y certifícate

Únete a nuestra plataforma de cursos de programación y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración