Introducción
Thymeleaf es un motor de plantillas de Java que puede trabajar directamente con Spring. Además de estas funciones básicas, nos ofrece un conjunto de objetos de utilidad que nos ayudarán a realizar tareas comunes en nuestra aplicación. En este tutorial, discutiremos el procesamiento y formato de las nuevas y antiguas clases de fecha de Java, utilizando un puñado de características de Thymeleaf 3.0.
Dependencias de Maven
Primero, vamos a crear la configuración para integrar Thymeleaf con Spring en nuestro archivo pom.xml
. Agregue las siguientes dependencias:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
Las versiones más recientes de thymeleaf y thymeleaf-spring5 se pueden encontrar en Maven Central. Tenga en cuenta que, para un proyecto de Spring 4, debe usar la biblioteca thymeleaf-spring4 en lugar de thymeleaf-spring5.
Además, para trabajar con las nuevas clases de fecha de Java 8, necesitaremos agregar otra dependencia a nuestro pom.xml
:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
El módulo de Thymeleaf extras es opcional, pero totalmente soportado por el equipo oficial de Thymeleaf, y fue creado para ser compatible con la API de tiempo de Java 8. Este módulo añade un objeto #temporals
al contexto como un procesador de objetos de utilidad durante la evaluación de expresiones.
Viejo y Nuevo: java.util y java.time
El paquete de tiempo es una nueva API de fecha, hora y calendario para la plataforma Java SE. La principal diferencia entre esta nueva API y la antigua API Date
es que la nueva API distingue entre las vistas de máquina y humana de una línea de tiempo. La vista de máquina revela una secuencia de valores enteros relativos a la época, mientras que la vista humana revela un conjunto de campos (por ejemplo, año, mes y día).
Para trabajar con el nuevo paquete de tiempo, necesitamos configurar nuestro motor de plantillas para utilizar el Java8TimeDialect
:
private ISpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.addDialect(new Java8TimeDialect());
engine.setTemplateResolver(templateResolver);
return engine;
}
Esto añadirá el objeto #temporals
, similar a los que se encuentran en el dialecto estándar, permitiendo la creación y el formato de objetos Temporal
desde las plantillas de Thymeleaf.
Ejemplo de prueba de procesamiento de clases de fecha
Para probar el procesamiento de las nuevas y viejas clases, crearemos las siguientes variables y las añadiremos como objetos de modelo a nuestra clase controlador:
model.addAttribute("standardDate", new Date());
model.addAttribute("localDateTime", LocalDateTime.now());
model.addAttribute("localDate", LocalDate.now());
model.addAttribute("timestamp", Instant.now());
Ahora, estamos listos para usar los objetos de utilidad de Expression
y Temporals
de Thymeleaf.
Formatear Fechas
La primera función que queremos cubrir es el formateo de un objeto Date
(que se añade a los parámetros del modelo de Spring). Usaremos el formato ISO8601:
<h1>Format ISO</h1>
<p th:text="${#dates.formatISO(standardDate)}"></p>
<p th:text="${#temporals.formatISO(localDateTime)}"></p>
<p th:text="${#temporals.formatISO(localDate)}"></p>
<p th:text="${#temporals.formatISO(timestamp)}"></p>
Sin importar cómo se haya configurado nuestra Date
en la parte posterior, se mostrará en Thymeleaf de acuerdo con el estándar seleccionado. La standardDate
será procesada por la utilidad #dates
. Las nuevas clases LocalDateTime
, LocalDate
y Instant
serán procesadas por la utilidad #temporals
.
Además, si queremos establecer el formato manualmente, podemos hacerlo usando:
<h1>Format manually</h1>
<p th:text="${#dates.format(standardDate, 'dd-MM-yyyy HH:mm')}"></p>
<p th:text="${#temporals.format(localDateTime, 'dd-MM-yyyy HH:mm')}"></p>
<p th:text="${#temporals.format(localDate, 'MM-yyyy')}"></p>
Como podemos observar, no podemos procesar la clase Instant
con #temporals.format(...)
— esto resultará en UnsupportedTemporalTypeException. Además, el formateo del LocalDate
sólo es posible si especificamos solo los campos de fecha particulares, omitiendo los campos de tiempo.
Obtener Campos de Fecha Específicos
Para obtener los campos específicos de la clase java.util.Date
, debemos usar los siguientes objetos de utilidad:
${#dates.day(date)}
${#dates.month(date)}
${#dates.monthName(date)}
${#dates.monthNameShort(date)}
${#dates.year(date)}
${#dates.dayOfWeek(date)}
${#dates.dayOfWeekName(date)}
${#dates.dayOfWeekNameShort(date)}
${#dates.hour(date)}
${#dates.minute(date)}
${#dates.second(date)}
${#dates.millisecond(date)}
Para el nuevo paquete java.time
, debemos ceñirnos a las utilidades #temporals
:
${#temporals.day(date)}
${#temporals.month(date)}
${#temporals.monthName(date)}
${#temporals.monthNameShort(date)}
${#temporals.year(date)}
${#temporals.dayOfWeek(date)}
${#temporals.dayOfWeekName(date)}
${#temporals.dayOfWeekNameShort(date)}
${#temporals.hour(date)}
${#temporals.minute(date)}
${#temporals.second(date)}
${#temporals.millisecond(date)}
Veamos algunos ejemplos. Primero, mostremos el día de la semana de hoy:
<h1>Show only which day of a week</h1>
<p th:text="${#dates.day(standardDate)}"></p>
<p th:text="${#temporals.day(localDateTime)}"></p>
<p th:text="${#temporals.day(localDate)}"></p>
A continuación, mostremos el nombre del día de la semana:
<h1>Show the name of the week day</h1>
<p th:text="${#dates.dayOfWeekName(standardDate)}"></p>
<p th:text="${#temporals.dayOfWeekName(localDateTime)}"></p>
<p th:text="${#temporals.dayOfWeekName(localDate)}"></p>
Finalmente, mostremos el segundo actual del día:
<h1>Show the second of the day</h1>
<p th:text="${#dates.second(standardDate)}"></p>
<p th:text="${#temporals.second(localDateTime)}"></p>
Tenga en cuenta que para trabajar con partes de tiempo, necesitamos usar LocalDateTime
, ya que LocalDate
generará un error.
Cómo Usar un Selector de Fecha en un Formulario
Ahora, veamos cómo usar un selector de fecha para enviar un valor de Date
desde un formulario Thymeleaf.
Primero, crearemos una clase Student
con un atributo Date
:
public class Student implements Serializable {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthDate;
}
La anotación @DateTimeFormat
declara que el campo birthDate
debe estar formateado como una Date
.
A continuación, crearemos un formulario Thymeleaf para enviar una entrada de Date
:
<form th:action="@{/saveStudent}" method="post" th:object="${student}">
<div>
<label for="student-birth-date">Date of birth:</label>
<input type="date" th:field="${student.birthDate}" id="student-birth-date"/>
</div>
<div>
<button type="submit" class="button">Submit</button>
</div>
</form>
Cuando enviemos el formulario, un controlador interceptará el objeto Student
mapeado en el formulario con el atributo th:object
. Además, el atributo th:field
enlaza el valor de entrada con el campo birthDate
.
Ahora, crearemos un controlador para interceptar la solicitud POST
:
@RequestMapping(value = "/saveStudent", method = RequestMethod.POST)
public String saveStudent(Model model, @ModelAttribute("student") Student student) {
model.addAttribute("student", student);
return "datePicker/displayDate.html";
}
Después de enviar el formulario, mostraremos el valor de birthDate
en otra página con el patrón dd/MM/yyyy
:
<h1>Student birth date</h1>
<p th:text="${#dates.format(student.birthDate, 'dd/MM/yyyy')}"></p>
El resultado mostrará nuestro formulario con un selector de fecha:
Después de enviar el formulario, veremos la fecha seleccionada:
Conclusión
En este tutorial, discutimos las características de procesamiento de fechas en Java implementadas en el marco Thymeleaf, versión 3.0. Para probar, nuestra sugerencia es jugar con el código en un navegador primero y luego revisar las pruebas existentes de JUnit.
Tenga en cuenta que nuestros ejemplos no cubren todas las opciones disponibles en Thymeleaf. Si desea aprender sobre todos los tipos de utilidades, eche un vistazo a nuestro artículo sobre expresiones de Spring y Thymeleaf.
Al final, dominar el uso de Thymeleaf con las API de Java 8 puede hacer que el desarrollo web en Java sea mucho más efectivo y fluido. ¡Sigue experimentando y aprendiendo!