Plantillas HTML5 en Java con Thymeleaf

En las aplicaciones web desarrolladas con Java siempre se ha contado con múltiples opciones a la hora de presentar contenido mediante plantillas.

Quizá la opción más básica es la ofrecida por JSP, donde definimos la presentación de nuestras páginas utilizando marcado directamente o mediante JSP tags. Posteriormente, han ido apareciendo opciones mucho más potentes como Apache Velocity u otras más generales como Freemarker. En cualquier caso, la lista de posibilidades como es habitual en Java suele ser larga.

En esta ocasión y aprovechando que el 9 de Febrero se publicó su versión 2.0, vamos a examinar Thymeleaf.

Características principales

Thymeleaf es un framework de templating que promete flexibilidad y adecuación a los nuevos estándares como HTML5. Entre sus muchas características, cabe destacar las siguientes:

  • Diseñado para XML, XHTML y HTML5, pero extensible a otros formatos.

  • Puede ser usado en cualquier ámbito, no sólo Web. No tiene dependencias con el API de servlets.

  • Tiene soporte para la internacionalización del contenido.

  • Ofrece una alto rendimiento gracias a la implementación de un sistema de caché

  • Integración con Spring

  • Fácil de utilizar y de integrar al sólo proporcionar atributos que enriquecen el marcado sin incorporar tags nuevos.

  • Soporte para la evaluación de expresiones: OGNL (módulo estándar) o SpEL (Spring).

Estructura mínima

Para comenzar, podemos definir una primera plantilla simple.html con Thymeleaf que recoja el valor de una variable titulo y lo muestre mediante un h1:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <body>
    <h1 th:text="${titulo}">Título principal de la página</h1>
  </body>
</html>

Para poder ejecutarla y pasarle el valor de las variables que necesita, debemos completar tres simples pasos.

Encontrar plantillas

El primero de los pasos es conseguir un TemplateResolver. Este interfaz es la base para que Thymeleaf sea capaz de encontrar la plantilla que queremos usar, incluso si esta se encuentra almacenada en la caché.

Tenemos varias implementaciones de este interfaz Java. Las más comunes son ServletContextTemplateResolver, para trabajar en el contexto de una aplicación web o ClassLoaderTemplateResolver para el resto de casos generales.

TemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setTemplateMode(‘HTML5’);
templateResolver.setSuffix(’.html’);
templateResolver.setCacheable(true);
templateResolver.setCacheTTLMs(3600000L);

Como vemos en el ejemplo de código, en todas estas implementaciones tendremos la posibilidad de indicar:

  • La ruta exacta al directorio que contiene nuestras plantillas (suffix).

  • El modo de trabajo que deseamos: XML, VALIDXML, XHTML, VALIDXHTML, HTML5 o LEGACYHTML5.

  • Si la caché está activa o no (recomendable poner esta opción a false durante las pruebas).

  • El tiempo de cacheo de las plantillas en milisegundos.

Obtener el motor de transformación que realice el procesamiento

Para obtener el motor de transformación, sólo debemos instanciarlo y pasarle el TemplateResolver construido en el apartado anterior:

TemplateEngine templateEngine = new TemplateEngine();
templateEngine.setTemplateResolver(templateResolver);

Pasar los parámetros y lanzar la transformación

Ya sólo nos queda preparar los parámetros que va a necesitar la plantilla diseñada. En este caso, únicamente necesitamos un parámetro llamado ‘titulo’, y ya podemos lanzar el procesamiento:

IContext context = new Context();
context.getVariables().put(‘titulo’, ‘Mi título en la plantilla’);
String result = templateEngine.process(‘simple’, context);

De nuevo, tenemos varias implementaciones del interface IContext en función de si nuestro entorno es web (WebContext) o no (Context).

Si examinamos el ejemplo anterior, veremos que al método ‘process’ del motor de procesamiento le pasamos el nombre del fichero que contiene la plantilla pero sin la extensión ‘.html’. Es necesario pasarle sólo ‘simple’, ya que en el primer apartado ya hemos establecido ‘.html’ como suffix.

En definitiva, el resultado de la transformación debería de ser finalmente el esperado:

<!DOCTYPE html>
<html>
  <body>
    <h1>Mi título en la plantilla</h1>
  </body>
</html>

Expresiones y el lenguaje OGNL

Thymeleaf soporta OGNL (Object-Graph Navigation Language) para la definición de expresiones y como forma de acceder a las variables que le proporcionamos a la plantilla como entrada.

Este es un lenguaje que, en el contexto de Thymeleaf, simplifica y flexibiliza la creación de plantillas. A continuación, veremos algunos ejemplos.

Variables simples

Establecidas a partir de un valor textual o de una variable:

<input type="text" name="nombre" value="Pedro Perez" th:value="${nombre}" />

O también a partir de un bean que contenga un método getter con el nombre de la referencia (getNombre):

<input type="text" name="nombre" value="Pedro Perez" th:value="${user.nombre}" />

Variables simples con soporte de internacionalización

<h1 th:text="#{bienvenida}">Texto de bienvenida</h1>

En este ejemplo, al preceder a la variable bienvenida con el caracter # en lugar del $, le indicamos al motor que establezca su valor en función del Locale establecido.

Para el Locale por defecto, buscará el valor de la variable ‘bienvenida’ en un fichero ‘.properties’ que deberá llamarse igual que la plantilla (simple.properties si tomamos como referencia el ejemplo inicial). Así, podremos proporcionar distintas versiones en función del idioma: simple_es.properties, simple_en.properties, etc

Todos estos ficheros de localización de cadenas, deben almacenarse en el mismo sitio que las plantillas.

En el código de nuestra aplicación, nuestro código en servidor será el siguiente:

String result = templateEngine.process("simple_multilang", new Context(new Locale("en")));

De forma que si tenemos un fichero simple_multilang_en.properties con el siguiente contenido:

bienvenida=Welcome to Genbeta Dev web!!

El resultado final obtenido será:

<h1>Welcome to Genbeta Dev web!!</h1>

Bucles

Con Thymeleaf podemos iterar de forma muy sencilla sobre una colección que recibimos como parámetro.

Por ejemplo, vamos a pasar un parámetro a la plantilla llamado productos. El valor de este parámetro será una colección definida por un ArrayList de instancias de tipo Producto. Producto es un bean y sus atributos principales son id y nombre. En este escenario, podemos iterar fácilmente sobre esta colección y mostrar todos sus valores:

…
  <tr th:each="prod : ${productos}">
    <td th:text="${prod.id}">Código del producto</td>
    <td th:text="${prod.nobre}">Nombre del producto</td>
  </tr>
…

De forma que simplemente creando la colección en el código del servidor:

Producto producto = new Producto();
producto.setId(1);
producto.setNombre("Producto 1");
IContext context = new Context();
context.getVariables().put("productos", Collections.singletonList(producto));
String result = templateEngine.process("colecciones", context);

Obtendremos la tabla resultado:

<!DOCTYPE html>
<html>
  <body>
    <table>
      <tr>
        <td>1</td>
        <td>Producto 1</td>
      </tr>
    </table>  
  </body>
</html>

Conclusiones

Thymeleaf es un motor de procesamiento muy flexible, adaptado a las necesidades actuales, poco intrusivo en el marcado y muy muy rápido. Si te han gustado las referencias a las estructuras básicas que se pueden emplear o si eres usuario de Spring Framework, no te pierdas la documentación oficial para una revisión de toda la potencia que ofrece.


Página del proyecto | Thymeleaf

Código de ejemplo | Github

Portada de Genbeta