Você está na página 1de 94

01/08/2020 Tutorial: Usando o Thymeleaf

Thymeleaf

Tutorial: Usando o Thymeleaf


Versão do documento: 20181029 - 29 outubro 2018
Versão do projeto: 3.0.11.RELEASE
Site do projeto: https://www.thymeleaf.org

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 1/94
01/08/2020 Tutorial: Usando o Thymeleaf

1 Introdução ao Thymeleaf

1.1 O que é o Thymeleaf?

O Thymeleaf é um moderno mecanismo de modelo Java do lado do servidor para ambientes web e independentes, capaz de
processar HTML, XML, JavaScript, CSS e até texto sem formatação.

O principal objetivo do Thymeleaf é fornecer uma maneira elegante e altamente sustentável de criar modelos. Para conseguir
isso, ele se baseia no conceito de Modelos Naturais para injetar sua lógica nos arquivos de modelo de uma maneira que não
afete o uso do modelo como protótipo de design. Isso melhora a comunicação do design e preenche a lacuna entre as
equipes de design e desenvolvimento.

O Thymeleaf também foi desenvolvido desde o início, tendo em mente os Padrões da Web - especialmente HTML5 -,
permitindo a criação de modelos de validação completa, se for necessário.

1.2 Que tipo de modelos o Thymeleaf pode processar?

Pronto para uso, o Thymeleaf permite processar seis tipos de modelos, cada um dos quais é chamado de Modo de Modelo :

HTML
XML
TEXTO
JAVASCRIPT
CSS
CRU

Existem dois modos de modelo de marcação ( HTML e XML ), três modos de modelo de texto ( TEXT , JAVASCRIPT e CSS ) e um
modo de modelo não operacional ( RAW ).

O HTMLmodo de modelo permitirá qualquer tipo de entrada HTML, incluindo HTML5, HTML 4 e XHTML. Nenhuma validação ou
veri cação de boa formação será executada e o código / estrutura do modelo será respeitado na maior extensão possível na
saída.

O XMLmodo de modelo permitirá a entrada XML. Nesse caso, espera-se que o código seja bem formado - sem tags não
fechadas, sem atributos não citados, etc. - e o analisador lançará exceções se forem encontradas violações de boa formação.
Observe que nenhuma validação (contra um esquema DTD ou XML) será executada.

O TEXTmodo de modelo permitirá o uso de uma sintaxe especial para modelos de natureza sem marcação. Exemplos de tais
modelos podem ser emails de texto ou documentação de modelo. Observe que os modelos HTML ou XML também podem
ser processados como TEXT ; nesse caso, eles não serão analisados como marcação e todas as tags, DOCTYPE, comentário,
etc, serão tratadas como mero texto.

O JAVASCRIPTmodo de modelo permitirá o processamento de arquivos JavaScript em um aplicativo Thymeleaf. Isso signi ca
poder usar os dados do modelo nos arquivos JavaScript da mesma maneira que nos arquivos HTML, mas com integrações
especí cas do JavaScript, como escape especializado ou script natural . O JAVASCRIPT modo de modelo é considerado um
modo de texto e, portanto, usa a mesma sintaxe especial que o TEXT modo de modelo.

O CSSmodo de modelo permitirá o processamento de arquivos CSS envolvidos em um aplicativo Thymeleaf. Semelhante ao
JAVASCRIPT modo, o CSS modo de modelo também é um modo de texto e usa a sintaxe de processamento especial do
TEXT modo de modelo.

O RAWmodo de modelo simplesmente não processa modelos. Ele deve ser usado para inserir recursos intocados (arquivos,
respostas de URL etc.) nos modelos que estão sendo processados. Por exemplo, recursos externos não controlados no
formato HTML podem ser incluídos nos modelos de aplicativos, sabendo com segurança que qualquer código Thymeleaf que
esses recursos possam incluir não será executado.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 2/94
01/08/2020 Tutorial: Usando o Thymeleaf

1.3 Dialetos: O Dialeto Padrão

O Thymeleaf é um mecanismo de modelo extremamente extensível (na verdade, pode ser chamado de estrutura do
mecanismo de modelo ) que permite de nir e personalizar a maneira como seus modelos serão processados com um nível de
detalhe no.

Um objeto que aplica alguma lógica a um artefato de marcação (uma tag, algum texto, um comentário ou um mero marcador
de posição se os modelos não forem marcados) é chamado de processador , e um conjunto desses processadores - além de
alguns artefatos extras - é o que um dialeto é normalmente composto. Prontamente, a biblioteca principal do Thymeleaf
fornece um dialeto chamado Dialeto Padrão , que deve ser su ciente para a maioria dos usuários.

Observe que os dialetos podem na verdade não ter processadores e ser totalmente compostos por outros tipos de
artefatos, mas os processadores são de nitivamente o caso de uso mais comum.

Este tutorial aborda o dialeto padrão . Cada atributo e recurso de sintaxe que você aprenderá nas páginas a seguir são
de nidos por esse dialeto, mesmo que isso não seja mencionado explicitamente.

Obviamente, os usuários podem criar seus próprios dialetos (mesmo estendendo o padrão) se quiserem de nir sua própria
lógica de processamento enquanto aproveitam os recursos avançados da biblioteca. O Thymeleaf também pode ser
con gurado para usar vários dialetos por vez.

Os pacotes o ciais de integração thymeleaf-spring3 e thymeleaf-spring4 de nem um dialeto chamado


“SpringStandard Dialect”, que é basicamente o mesmo que o Standard Dialect, mas com pequenas adaptações para
aproveitar melhor alguns recursos do Spring Framework (por exemplo, , usando Spring Expression Language ou
SpringEL em vez de OGNL). Portanto, se você é um usuário do Spring MVC, não está perdendo tempo, pois quase tudo
que aprender aqui será útil em seus aplicativos Spring.

A maioria dos processadores do Dialeto Padrão são processadores de atributos . Isso permite que os navegadores exibam
corretamente os arquivos de modelo HTML, mesmo antes de serem processados, porque simplesmente ignoram os atributos
adicionais. Por exemplo, enquanto um JSP usando bibliotecas de tags pode incluir um fragmento de código não diretamente
exibido por um navegador como:

<form:inputText name="userName" value="${user.name}" />

… O Thymeleaf Standard Dialect nos permitiria alcançar a mesma funcionalidade com:

<input type="text" name="userName" value="James Carrot" th:value="${user.name}" />

Isso não apenas será exibido corretamente pelos navegadores, mas também nos permitirá (opcionalmente) especi car um
atributo de valor ("James Carrot", neste caso) que será exibido quando o protótipo for estaticamente aberto em um
navegador e que será substituído pelo valor resultante da avaliação ${user.name} durante o processamento do modelo.

Isso ajuda seu designer e desenvolvedor a trabalhar no mesmo arquivo de modelo e reduzir o esforço necessário para
transformar um protótipo estático em um arquivo de modelo de trabalho. A capacidade de fazer isso é um recurso chamado
Natural Templating .

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 3/94
01/08/2020 Tutorial: Usando o Thymeleaf

2 O mantimento virtual Good Thymes


O código-fonte dos exemplos mostrados neste e os próximos capítulos deste guia podem ser encontrados no repositório do
GitHub Good Thymes Virtual Grocery .

2.1 Um site para uma mercearia

Para explicar melhor os conceitos envolvidos no processamento de modelos com o Thymeleaf, este tutorial utilizará um
aplicativo de demonstração, que pode ser baixado do site do projeto.

Este aplicativo é o site de uma mercearia virtual imaginária e fornecerá muitos cenários para mostrar os muitos recursos do
Thymeleaf.

Para começar, precisamos de um conjunto simples de entidades de modelo para nosso aplicativo: as Products quais são
vendidas Customers através da criação Orders . Também gerenciaremos Comments sobre Products :

Modelo de aplicativo de exemplo

Nosso aplicativo também terá uma camada de serviço muito simples, composta por Service objetos contendo métodos
como:

public class ProductService {

...

public List<Product> findAll() {

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 4/94
01/08/2020 Tutorial: Usando o Thymeleaf
return ProductRepository.getInstance().findAll();
}

public Product findById(Integer id) {


return ProductRepository.getInstance().findById(id);
}

Na camada web, nosso aplicativo terá um ltro que delegará a execução aos comandos habilitados para Thymeleaf,
dependendo da URL da solicitação:

private boolean process(HttpServletRequest request, HttpServletResponse response)


throws ServletException {

try {

// This prevents triggering engine executions for resource URLs


if (request.getRequestURI().startsWith("/css") ||
request.getRequestURI().startsWith("/images") ||
request.getRequestURI().startsWith("/favicon")) {
return false;
}

/*
* Query controller/URL mapping and obtain the controller
* that will process the request. If no controller is available,
* return false and let other filters/servlets process the request.
*/
IGTVGController controller = this.application.resolveControllerForRequest(request);
if (controller == null) {
return false;
}

/*
* Obtain the TemplateEngine instance.
*/
ITemplateEngine templateEngine = this.application.getTemplateEngine();

/*
* Write the response headers
*/
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);

/*
* Execute the controller and process view template,
* writing the results to the response writer.
*/
controller.process(
request, response, this.servletContext, templateEngine);

return true;

} catch (Exception e) {
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (final IOException ignored) {
// Just ignore this
}
throw new ServletException(e);
}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 5/94
01/08/2020 Tutorial: Usando o Thymeleaf

Esta é a nossa IGTVGController interface:

public interface IGTVGController {

public void process(


HttpServletRequest request, HttpServletResponse response,
ServletContext servletContext, ITemplateEngine templateEngine);

Tudo o que precisamos fazer agora é criar implementações da IGTVGController interface, recuperando dados dos serviços e
processando modelos usando o ITemplateEngine objeto.

No nal, cará assim:

Página inicial do aplicativo de exemplo

Mas primeiro vamos ver como esse mecanismo de modelo é inicializado.

2.2 Criando e con gurando o Template Engine

O método process (…) em nosso ltro continha esta linha:

ITemplateEngine templateEngine = this.application.getTemplateEngine();

O que signi ca que a classe GTVGApplication é responsável por criar e con gurar um dos objetos mais importantes em um
aplicativo Thymeleaf: a TemplateEngine instância (implementação da ITemplateEngine interface).

Nosso org.thymeleaf.TemplateEngine objeto é inicializado assim:

public class GTVGApplication {

...
private final TemplateEngine templateEngine;
...

public GTVGApplication(final ServletContext servletContext) {

super();

ServletContextTemplateResolver templateResolver =
new ServletContextTemplateResolver(servletContext);
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 6/94
01/08/2020 Tutorial: Usando o Thymeleaf

// HTML is the default mode, but we set it anyway for better understanding of code
templateResolver.setTemplateMode(TemplateMode.HTML);
// This will convert "home" to "/WEB-INF/templates/home.html"
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
// Template cache TTL=1h. If not set, entries would be cached until expelled
templateResolver.setCacheTTLMs(Long.valueOf(3600000L));

// Cache is set to true by default. Set to false if you want templates to


// be automatically updated when modified.
templateResolver.setCacheable(true);

this.templateEngine = new TemplateEngine();


this.templateEngine.setTemplateResolver(templateResolver);

...

Existem várias maneiras de con gurar um TemplateEngine objeto, mas por enquanto essas poucas linhas de código nos
ensinam o su ciente sobre as etapas necessárias.

O resolvedor de modelos

Vamos começar com o Resolvedor de modelos:

ServletContextTemplateResolver templateResolver =
new ServletContextTemplateResolver(servletContext);

Os resolvedores de modelos são objetos que implementam uma interface da API Thymeleaf chamada
org.thymeleaf.templateresolver.ITemplateResolver :

public interface ITemplateResolver {

...

/*
* Templates are resolved by their name (or content) and also (optionally) their
* owner template in case we are trying to resolve a fragment for another template.
* Will return null if template cannot be handled by this template resolver.
*/
public TemplateResolution resolveTemplate(
final IEngineConfiguration configuration,
final String ownerTemplate, final String template,
final Map<String, Object> templateResolutionAttributes);
}

Esses objetos são responsáveis por determinar como nossos modelos serão acessados e, neste aplicativo GTVG, os
org.thymeleaf.templateresolver.ServletContextTemplateResolver meios para recuperar nossos arquivos de modelo como
recursos do Servlet Context : um javax.servlet.ServletContext objeto de todo o aplicativo que existe em todos os
aplicativos da Web Java , e isso resolve recursos da raiz do aplicativo Web.

Mas isso não é tudo o que podemos dizer sobre o resolvedor de modelos, porque podemos de nir alguns parâmetros de
con guração. Primeiro, o modo de modelo:

templateResolver.setTemplateMode(TemplateMode.HTML);

HTML é o modo de modelo padrão ServletContextTemplateResolver , mas é uma boa prática estabelecê-lo de qualquer
maneira, para que nosso código documente claramente o que está acontecendo.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 7/94
01/08/2020 Tutorial: Usando o Thymeleaf

templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");

O pre xo e o su xo modi cam os nomes dos modelos que passaremos para o mecanismo para obter os nomes reais dos
recursos a serem usados.

Usando essa con guração, o nome do modelo "produto / lista" corresponderia a:

servletContext.getResourceAsStream("/WEB-INF/templates/product/list.html")

Opcionalmente, a quantidade de tempo que um modelo analisado pode viver no cache é con gurada no Resolvedor de
Modelos por meio da propriedade cacheTTLMs :

templateResolver.setCacheTTLMs(3600000L);

Um modelo ainda pode ser expulso do cache antes que o TTL seja atingido se o tamanho máximo do cache for atingido e a
entrada mais antiga atualmente em cache.

O comportamento e os tamanhos do cache podem ser de nidos pelo usuário implementando a


ICacheManager interface ou modi cando o StandardCacheManager objeto para gerenciar o cache padrão.

Há muito mais a aprender sobre os resolvedores de modelos, mas, por enquanto, vamos dar uma olhada na criação do nosso
objeto Template Engine.

O mecanismo de modelo

Os objetos do Template Engine são implementações da org.thymeleaf.ITemplateEngine interface. Uma dessas


implementações é oferecida pelo núcleo Thymeleaf:, org.thymeleaf.TemplateEngine e criamos uma instância dele aqui:

templateEngine = new TemplateEngine();


templateEngine.setTemplateResolver(templateResolver);

Bastante simples, não é? Tudo o que precisamos é criar uma instância e de nir o Resolvedor de modelos para ela.

Um resolvedor de modelo é o único parâmetro necessário TemplateEngine necessário, embora existam muitos outros que
serão abordados posteriormente (resolvedores de mensagens, tamanhos de cache, etc.). Por enquanto, é tudo o que
precisamos.

Nosso Template Engine está pronto e podemos começar a criar nossas páginas usando o Thymeleaf.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 8/94
01/08/2020 Tutorial: Usando o Thymeleaf

3 Usando textos

3.1 Boas-vindas em vários idiomas

Nossa primeira tarefa será criar uma home page para o nosso site de compras.

A primeira versão desta página será extremamente simples: apenas um título e uma mensagem de boas-vindas. Este é o
nosso /WEB-INF/templates/home.html arquivo:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<body>

<p th:text="#{home.welcome}">Welcome to our grocery store!</p>

</body>

</html>

A primeira coisa que você notará é que esse arquivo é HTML5 que pode ser exibido corretamente por qualquer navegador
porque não inclui nenhuma tag não HTML (os navegadores ignoram todos os atributos que eles não entendem, como
th:text ).

Mas você também pode observar que esse modelo não é realmente um documento HTML5 válido , porque esses atributos
não padrão que estamos usando no th:* formulário não são permitidos pela especi cação HTML5. De fato, estamos
adicionando um xmlns:th atributo à nossa <html> tag, algo absolutamente não HTML5-ish:

<html xmlns:th="http://www.thymeleaf.org">

… Que não tem nenhuma in uência no processamento de modelos, mas funciona como um encantamento que impede que
nosso IDE se queixe da falta de uma de nição de namespace para todos esses th:* atributos.

E se quiséssemos tornar esse modelo válido para HTML5 ? Fácil: alterne para a sintaxe do atributo de dados do Thymeleaf,
usando o data- pre xo para nomes de atributos e - separadores de hífen ( ) em vez de ponto e vírgula ( : ):

<!DOCTYPE html>

<html>

<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../css/gtvg.css" data-th-href="@{/css/gtvg.css}" />
</head>

<body>

<p data-th-text="#{home.welcome}">Welcome to our grocery store!</p>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 9/94
01/08/2020 Tutorial: Usando o Thymeleaf
</body>

</html>

data- Atributos pre xados personalizados são permitidos pela especi cação HTML5; portanto, com este código acima, nosso
modelo seria um documento HTML5 válido .

Ambas as notações são completamente equivalentes e intercambiáveis, mas por uma questão de simplicidade e
compactação dos exemplos de código, este tutorial usará a notação de espaço para nome ( th:* ). Além disso, a
th:* notação é mais geral e permitida em todos os modos de modelo do Thymeleaf ( XML , TEXT …), enquanto a
data- notação é permitida apenas no HTML modo.

Usando th: text e externalizando o texto

A externalização de texto está extraindo fragmentos de código de modelo de arquivos de modelo para que possam ser
mantidos em arquivos separados (normalmente .properties arquivos) e que possam ser facilmente substituídos por textos
equivalentes escritos em outros idiomas (um processo chamado internacionalização ou simplesmente i18n ). Fragmentos de
texto externalizados são geralmente chamados de "mensagens" .

As mensagens sempre têm uma chave que as identi ca, e o Thymeleaf permite especi car que um texto corresponda a uma
mensagem especí ca com a #{...} sintaxe:

<p th:text="#{home.welcome}">Welcome to our grocery store!</p>

O que podemos ver aqui são, de fato, duas características diferentes do Dialeto Padrão Thymeleaf:

O th:text atributo, que avalia sua expressão de valor e de ne o resultado como o corpo da tag host, substituindo
efetivamente o "Bem-vindo ao nosso supermercado!" texto que vemos no código.
A #{home.welcome} expressão, especi cada na Sintaxe de expressão padrão , instruindo que o texto a ser usado pelo
th:text atributo deve ser a mensagem com a home.welcome chave correspondente ao local com o qual estamos
processando o modelo.

Agora, onde está esse texto externalizado?

A localização do texto externalizado no Thymeleaf é totalmente con gurável e dependerá da


org.thymeleaf.messageresolver.IMessageResolver implementação especí ca que está sendo usada. Normalmente, uma
implementação baseada em .properties arquivos será usada, mas poderíamos criar nossas próprias implementações se
quiséssemos, por exemplo, obter mensagens de um banco de dados.

No entanto, não especi camos um resolvedor de mensagens para o nosso mecanismo de modelo durante a inicialização, e
isso signi ca que nosso aplicativo está usando o Resolvedor de Mensagens Padrão , implementado por
org.thymeleaf.messageresolver.StandardMessageResolver .

O resolvedor de mensagens padrão espera encontrar mensagens /WEB-INF/templates/home.html nos arquivos de


propriedades na mesma pasta e com o mesmo nome que o modelo, como:

/WEB-INF/templates/home_en.properties para textos em inglês.


/WEB-INF/templates/home_es.properties para textos em espanhol.
/WEB-INF/templates/home_pt_BR.properties para textos em português (Brasil).
/WEB-INF/templates/home.properties para textos padrão (se o código do idioma não corresponder).

Vamos dar uma olhada no nosso home_es.properties arquivo:

home.welcome=¡Bienvenido a nuestra tienda de comestibles!

É tudo o que precisamos para tornar o Thymeleaf o nosso modelo. Vamos criar nosso controlador doméstico então.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 10/94
01/08/2020 Tutorial: Usando o Thymeleaf

Contextos

Para processar nosso modelo, criaremos uma HomeController classe implementando a IGTVGController interface que vimos
antes:

public class HomeController implements IGTVGController {

public void process(


final HttpServletRequest request, final HttpServletResponse response,
final ServletContext servletContext, final ITemplateEngine templateEngine)
throws Exception {

WebContext ctx =
new WebContext(request, response, servletContext, request.getLocale());

templateEngine.process("home", ctx, response.getWriter());

A primeira coisa que vemos é a criação de um contexto . Um contexto Thymeleaf é um objeto que implementa a
org.thymeleaf.context.IContext interface. Os contextos devem conter todos os dados necessários para uma execução do
mecanismo de modelo em um mapa de variáveis e também fazer referência ao código do idioma que deve ser usado para
mensagens externalizadas.

public interface IContext {

public Locale getLocale();


public boolean containsVariable(final String name);
public Set<String> getVariableNames();
public Object getVariable(final String name);

Existe uma extensão especializada dessa interface, org.thymeleaf.context.IWebContext destinada a ser usada em aplicativos
Web baseados em ServletAPI (como o SpringMVC).

public interface IWebContext extends IContext {

public HttpServletRequest getRequest();


public HttpServletResponse getResponse();
public HttpSession getSession();
public ServletContext getServletContext();

A biblioteca principal do Thymeleaf oferece uma implementação de cada uma destas interfaces:

org.thymeleaf.context.Context implementa IContext


org.thymeleaf.context.WebContext implementa IWebContext

E como você pode ver no código do controlador, WebContext é o que usamos. De fato, temos que fazê-lo, porque o uso de a
ServletContextTemplateResolver requer o uso de uma implementação de contexto IWebContext .

WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());

Apenas três desses quatro argumentos do construtor são necessários porque o código de idioma padrão do sistema será
usado se nenhum for especi cado (embora você nunca permita que isso aconteça em aplicativos reais).

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 11/94
01/08/2020 Tutorial: Usando o Thymeleaf

Existem algumas expressões especializadas que poderemos usar para obter os parâmetros de solicitação e os atributos de
solicitação, sessão e aplicativo WebContext nos nossos modelos. Por exemplo:

${x} retornará uma variável x armazenada no contexto Thymeleaf ou como um atributo de solicitação .
${param.x} retornará um parâmetro de solicitação chamado x (que pode ter vários valores).
${session.x} retornará um atributo de sessão chamado x .
${application.x} retornará um atributo de contexto de servlet chamado x .

Executando o mecanismo de modelo

Com o nosso objeto de contexto pronto, agora podemos dizer ao mecanismo do modelo para processar o modelo (por seu
nome) usando o contexto e passando a ele um gravador de respostas para que a resposta possa ser gravada nele:

templateEngine.process("home", ctx, response.getWriter());

Vamos ver os resultados disso usando o código do idioma espanhol:

<!DOCTYPE html>

<html>

<head>
<title>Good Thymes Virtual Grocery</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
</head>

<body>

<p>¡Bienvenido a nuestra tienda de comestibles!</p>

</body>

</html>

3.2 Mais sobre textos e variáveis

Texto sem escape

A versão mais simples da nossa página inicial parece estar pronta agora, mas há algo em que não pensamos ... e se tivéssemos
uma mensagem como essa?

home.welcome=Welcome to our <b>fantastic</b> grocery store!

Se executarmos esse modelo como antes, obteremos:

<p>Welcome to our &lt;b&gt;fantastic&lt;/b&gt; grocery store!</p>

O que não é exatamente o que esperávamos, porque nossa <b> tag foi escapada e, portanto, será exibida no navegador.

Este é o comportamento padrão do th:text atributo. Se quisermos que o Thymeleaf respeite nossas tags HTML e não as
escape, teremos que usar um atributo diferente: th:utext (para "texto sem escape"):

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 12/94
01/08/2020 Tutorial: Usando o Thymeleaf

Isso exibirá nossa mensagem exatamente como queríamos:

<p>Welcome to our <b>fantastic</b> grocery store!</p>

Usando e exibindo variáveis

Agora vamos adicionar mais conteúdo à nossa página inicial. Por exemplo, podemos exibir a data abaixo da nossa mensagem
de boas-vindas, assim:

Welcome to our fantastic grocery store!

Today is: 12 july 2010

Primeiro, teremos que modi car nosso controlador para adicionar essa data como uma variável de contexto:

public void process(


final HttpServletRequest request, final HttpServletResponse response,
final ServletContext servletContext, final ITemplateEngine templateEngine)
throws Exception {

SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");


Calendar cal = Calendar.getInstance();

WebContext ctx =
new WebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));

templateEngine.process("home", ctx, response.getWriter());

Adicionamos uma String variável chamada today ao nosso contexto e agora podemos exibi-la em nosso modelo:

<body>

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

<p>Today is: <span th:text="${today}">13 February 2011</span></p>

</body>

Como você pode ver, ainda estamos usando o th:text atributo para o trabalho (e isso está correto, porque queremos
substituir o corpo da tag), mas a sintaxe é um pouco diferente dessa vez e, em vez de um #{...} valor de expressão, estamos
usando um ${...} 1. Essa é uma expressão variável e contém uma expressão em uma linguagem chamada OGNL (Object-
Graph Navigation Language) que será executada no mapa de variáveis de contexto de que falamos anteriormente.

A ${today} expressão simplesmente signi ca "obter a variável chamada hoje", mas essas expressões podem ser mais
complexas (como ${user.name} em "obter a variável chamada usuário e chamar seu getName() método").

Existem muitas possibilidades em valores de atributos: mensagens, expressões variáveis ... e muito mais. O próximo capítulo
nos mostrará quais são todas essas possibilidades.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 13/94
01/08/2020 Tutorial: Usando o Thymeleaf

4 Sintaxe de expressão padrão


Faremos uma pequena pausa no desenvolvimento de nossa loja virtual de supermercado para aprender sobre uma das partes
mais importantes do Dialeto Padrão Thymeleaf: a sintaxe da Expressão Padrão Thymeleaf.

Já vimos dois tipos de valores de atributos válidos expressos nesta sintaxe: mensagem e expressões variáveis:

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

<p>Today is: <span th:text="${today}">13 february 2011</span></p>

Mas existem mais tipos de expressões e detalhes mais interessantes para aprender sobre os que já conhecemos. Primeiro,
vamos ver um rápido resumo dos recursos da Expressão padrão:

Expressões simples:

Expressões variáveis: ${...}


Expressões de variáveis de seleção: *{...}
Expressões da mensagem: #{...}
Expressões de URL de link: @{...}
Expressões de fragmentos: ~{...}

Literais

Literais de texto: 'one text' , 'Another one!' , ...


Literais Número: 0 , 34 , 3.0 , 12.3 , ...
Literais booleanos: true , false
Nulo literal: null
Símbolos literais: one , sometext , main , ...

Operações de texto:

Concatenação de cadeias: +
Substituições literais: |The name is ${name}|

Operaçoes aritimeticas:

Operadores binários: + , - , * , / , %
Sinal de menos (operador unário): -

Operações booleanas:

Operadores binários: and , or


Negação booleano (operador unária): ! , not

Comparações e igualdade:

Comparadores: > , < , >= , <= ( gt , lt , ge , le )


Operadores de igualdade: == , != ( eq , ne )

Operadores condicionais:

Se então: (if) ? (then)


Se-então-outro: (if) ? (then) : (else)
Padrão: (value) ?: (defaultvalue)

Fichas especiais:

Nenhuma operação: _

Todos esses recursos podem ser combinados e aninhados:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 14/94
01/08/2020 Tutorial: Usando o Thymeleaf

'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

4.1 Mensagens

Como já sabemos, as #{...} expressões de mensagem nos permitem vincular isso:

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

…para isso:

home.welcome=¡Bienvenido a nuestra tienda de comestibles!

Mas há um aspecto em que ainda não pensamos: o que acontece se o texto da mensagem não for completamente estático? E
se, por exemplo, nosso aplicativo soubesse quem é o usuário que está visitando o site a qualquer momento e desejássemos
cumprimentá-lo pelo nome?

<p>¡Bienvenido a nuestra tienda de comestibles, John Apricot!</p>

Isso signi ca que precisaríamos adicionar um parâmetro à nossa mensagem. Bem assim:

home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!

Os parâmetros são especi cados de acordo com a java.text.MessageFormatsintaxe padrão, o que signi ca que você pode
formatar para números e datas, conforme especi cado nos documentos da API para classes no java.text.* pacote.

Para especi car um valor para o nosso parâmetro e receber um atributo de sessão HTTP chamado user , poderíamos ter:

<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>

Observe que o uso th:utext aqui signi ca que a mensagem formatada não será escapada. Este exemplo supõe que
user.name já tenha escapado.

Vários parâmetros podem ser especi cados, separados por vírgulas.

A chave da mensagem em si pode vir de uma variável:

<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>

4.2 Variáveis

Já mencionamos que as ${...} expressões são de fato expressões OGNL (Object-Graph Navigation Language) executadas no
mapa de variáveis contidas no contexto.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 15/94
01/08/2020 Tutorial: Usando o Thymeleaf

Para obter informações detalhadas sobre sintaxe e recursos do OGNL, leia o Guia de idiomas do OGNL.

Nos aplicativos habilitados para Spring MVC, o OGNL será substituído pelo SpringEL , mas sua sintaxe é muito
semelhante à do OGNL (na verdade, exatamente a mesma para os casos mais comuns).

Da sintaxe do OGNL, sabemos que a expressão em:

<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>

… É de fato equivalente a isso:

ctx.getVariable("today");

Mas o OGNL nos permite criar expressões muito mais poderosas, e é assim:

<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>

… Obtém o nome de usuário executando:

((User) ctx.getVariable("session").get("user")).getName();

Mas a navegação do método getter é apenas um dos recursos do OGNL. Vamos ver um pouco mais:

/*
* Access to properties using the point (.). Equivalent to calling property getters.
*/
${person.father.name}

/*
* Access to properties can also be made by using brackets ([]) and writing
* the name of the property as a variable or between single quotes.
*/
${person['father']['name']}

/*
* If the object is a map, both dot and bracket syntax will be equivalent to
* executing a call on its get(...) method.
*/
${countriesByCode.ES}
${personsByName['Stephen Zucchini'].age}

/*
* Indexed access to arrays or collections is also performed with brackets,
* writing the index without quotes.
*/
${personsArray[0].name}

/*
* Methods can be called, even with arguments.
*/
${person.createCompleteName()}
${person.createCompleteNameWithSeparator('-')}

Objetos básicos de expressão

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 16/94
01/08/2020 Tutorial: Usando o Thymeleaf

Ao avaliar expressões OGNL nas variáveis de contexto, alguns objetos são disponibilizados para expressões para maior
exibilidade. Esses objetos serão referenciados (por padrão OGNL) começando com o # símbolo:

#ctx : o objeto de contexto.


#vars: as variáveis de contexto.
#locale : o código do idioma do contexto.
#request : (apenas em contextos da Web) o HttpServletRequest objeto.
#response : (apenas em contextos da Web) o HttpServletResponse objeto.
#session : (apenas em contextos da Web) o HttpSession objeto.
#servletContext : (apenas em contextos da Web) o ServletContext objeto.

Para que possamos fazer isso:

Established locale country: <span th:text="${#locale.country}">US</span>.

Você pode ler a referência completa desses objetos no Apêndice A .

Objetos do utilitário de expressão

Além desses objetos básicos, o Thymeleaf nos oferece um conjunto de objetos utilitários que nos ajudarão a executar tarefas
comuns em nossas expressões.

#execInfo : informações sobre o modelo que está sendo processado.


#messages : métodos para obter mensagens externalizadas dentro de expressões de variáveis, da mesma maneira que
seriam obtidas usando a sintaxe # {…}.
#uris : métodos para escapar partes de URLs / URIs
#conversions : métodos para executar o serviço de conversão con gurado (se houver).
#dates : métodos para java.util.Date objetos: formatação, extração de componentes, etc.
#calendars : análogo a #dates , mas para java.util.Calendar objetos.
#numbers : métodos para formatar objetos numéricos.
#strings : métodos para String objetos: contém, começa com, acrescentando / acrescentando, etc.
#objects : métodos para objetos em geral.
#bools : métodos para avaliação booleana.
#arrays : métodos para matrizes.
#lists : métodos para listas.
#sets : métodos para conjuntos.
#maps : métodos para mapas.
#aggregates : métodos para criar agregados em matrizes ou coleções.
#ids : métodos para lidar com atributos de ID que podem ser repetidos (por exemplo, como resultado de uma iteração).

Você pode veri car quais as funções são oferecidos por cada um desses objetos utilitários no Apêndice B .

Reformatação de datas em nossa home page

Agora que sabemos sobre esses objetos utilitários, podemos usá-los para alterar a maneira como mostramos a data em nossa
página inicial. Em vez de fazer isso em nosso HomeController :

SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");


Calendar cal = Calendar.getInstance();

WebContext ctx = new WebContext(request, servletContext, request.getLocale());


ctx.setVariable("today", dateFormat.format(cal.getTime()));

templateEngine.process("home", ctx, response.getWriter());

... podemos fazer exatamente isso:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 17/94
01/08/2020 Tutorial: Usando o Thymeleaf

WebContext ctx =
new WebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", Calendar.getInstance());

templateEngine.process("home", ctx, response.getWriter());

… E execute a formatação da data na própria camada de exibição:

<p>
Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011</span>
</p>

4.3 Expressões nas seleções (sintaxe do asterisco)

Não apenas expressões variáveis podem ser escritas como ${...} , mas também como *{...} .

Há uma diferença importante: a sintaxe do asterisco avalia expressões em objetos selecionados, e não em todo o contexto. Ou
seja, desde que não haja um objeto selecionado, as sintaxes do dólar e do asterisco fazem exatamente o mesmo.

E o que é um objeto selecionado? O resultado de uma expressão usando o th:object atributo Vamos usar um em nossa
userprofile.html página de per l de usuário ( ):

<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

O que é exatamente equivalente a:

<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>

Obviamente, a sintaxe do dólar e do asterisco pode ser misturada:

<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

Quando uma seleção de objeto está em vigor, o objeto selecionado também estará disponível para expressões em dólar
como a #object variável de expressão:

<div th:object="${session.user}">
<p>Name: <span th:text="${#object.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

Como dito, se nenhuma seleção de objeto foi realizada, as sintaxes do dólar e do asterisco são equivalentes.

<div>
<p>Name: <span th:text="*{session.user.name}">Sebastian</span>.</p>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 18/94
01/08/2020 Tutorial: Usando o Thymeleaf
<p>Surname: <span th:text="*{session.user.surname}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{session.user.nationality}">Saturn</span>.</p>
</div>

4.4 URLs de link

Devido à sua importância, os URLs são cidadãos de primeira classe nos modelos de aplicativos da Web, e o Thymeleaf
Standard Dialect possui uma sintaxe especial para eles, a @ sintaxe: @{...}

Existem diferentes tipos de URLs:

URLs absolutos: http://www.thymeleaf.org


URLs relativos, que podem ser:

Relativo à página: user/login.html


Relativo ao /itemdetails?id=3 contexto : (o nome do contexto no servidor será adicionado automaticamente)
Relativo ao servidor: ~/billing/processInvoice (permite chamar URLs em outro contexto (= aplicativo) no mesmo
servidor.
URLs relativos ao protocolo: //code.jquery.com/jquery-2.0.3.min.js

O processamento real dessas expressões e sua conversão nas URLs que serão produzidas são feitas pelas implementações da
org.thymeleaf.linkbuilder.ILinkBuilder interface que são registradas no ITemplateEngine objeto que está sendo usado.

Por padrão, uma única implementação dessa interface é registrada da classe


org.thymeleaf.linkbuilder.StandardLinkBuilder , o que é su ciente para cenários o ine (não da Web) e também da Web
com base na API do Servlet. Outros cenários (como a integração com estruturas da web não ServletAPI) podem precisar de
implementações especí cas da interface do construtor de links.

Vamos usar essa nova sintaxe. Conheça o th:href atributo:

<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->


<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>

<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->


<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>

<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->


<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

Algumas coisas a serem observadas aqui:

th:href é um atributo modi cador: uma vez processado, ele calculará o URL do link a ser usado e de nirá esse valor para
o href atributo da <a> tag.
Temos permissão para usar expressões para parâmetros de URL (como você pode ver em orderId=${o.id} ). As
operações de codi cação de parâmetro de URL necessárias também serão executadas automaticamente.
Se vários parâmetros forem necessários, eles serão separados por vírgulas:
@{/order/process(execId=${execId},execType='FAST')}
Modelos variáveis também são permitidos nos caminhos de URL: @{/order/{orderId}/details(orderId=${orderId})}
URLs relativos que começam com / (por exemplo /order/details :) serão pre xados automaticamente pelo nome do
contexto do aplicativo.
Se os cookies não estiverem ativados ou isso ainda não for conhecido, um ";jsessionid=..." su xo poderá ser
adicionado aos URLs relativos para que a sessão seja preservada. Isso se chama Reescrita de URL e o Thymeleaf permite
conectar seus próprios ltros de reescrita usando o response.encodeURL(...) mecanismo da API do Servlet para cada
URL.
O th:href atributo nos permite (opcionalmente) ter um href atributo estático ativo em nosso modelo, para que os links
de nosso modelo permaneçam navegáveis por um navegador quando abertos diretamente para ns de prototipagem.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 19/94
01/08/2020 Tutorial: Usando o Thymeleaf

Como foi o caso da sintaxe da mensagem ( #{...} ), as bases de URL também podem ser o resultado da avaliação de outra
expressão:

<a th:href="@{${url}(orderId=${o.id})}">view</a>
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>

Um menu para a nossa página inicial

Agora que sabemos como criar URLs de link, que tal adicionar um pequeno menu em nossa página inicial para algumas das
outras páginas do site?

<p>Please select an option</p>


<ol>
<li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li>
<li><a href="order/list.html" th:href="@{/order/list}">Order List</a></li>
<li><a href="subscribe.html" th:href="@{/subscribe}">Subscribe to our Newsletter</a></li>
<li><a href="userprofile.html" th:href="@{/userprofile}">See User Profile</a></li>
</ol>

URLs relativos à raiz do servidor

Uma sintaxe adicional pode ser usada para criar URLs relativas à raiz do servidor (em vez de relativas à raiz de contexto), a m
de vincular a diferentes contextos no mesmo servidor. Esses URLs serão especi cados como @{~/path/to/something}

4.5 Fragmentos

As expressões de fragmento são uma maneira fácil de representar fragmentos de marcação e movê-los pelos modelos. Isso
nos permite replicá-los, passá-los para outros modelos como argumentos e assim por diante.

O uso mais comum é para inserção de fragmentos usando th:insert ou th:replace (mais sobre isso em uma seção
posterior):

<div th:insert="~{commons :: main}">...</div>

Mas eles podem ser usados em qualquer lugar, assim como qualquer outra variável:

<div th:with="frag=~{footer :: #main/text()}">


<p th:insert="${frag}">
</div>

Posteriormente neste tutorial, há uma seção inteira dedicada ao Layout do modelo, incluindo uma explicação mais profunda
das expressões de fragmento.

4.6 Literais

Literais de texto

Literais de texto são apenas cadeias de caracteres especi cadas entre aspas simples. Eles podem incluir qualquer caractere,
mas você deve escapar de aspas simples dentro deles usando \' .

<p>
Now you are looking at a <span th:text="'working web application'">template file</span>.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 20/94
01/08/2020 Tutorial: Usando o Thymeleaf
</p>

Literais numéricos

Literais numéricos são apenas isso: números.

<p>The year is <span th:text="2013">1492</span>.</p>


<p>In two years, it will be <span th:text="2013 + 2">1494</span>.</p>

Literais booleanos

Os literais booleanos são true e false . Por exemplo:

<div th:if="${user.isAdmin()} == false"> ...

Neste exemplo, o == false é escrito fora do aparelho e, portanto, é a Thymeleaf que cuida dele. Se fosse escrito dentro do
aparelho, seria de responsabilidade dos motores OGNL / SpringEL:

<div th:if="${user.isAdmin() == false}"> ...

O literal nulo

O null literal também pode ser usado:

<div th:if="${variable.something} == null"> ...

Tokens literais

Literais numéricos, booleanos e nulos são, de fato, um caso particular de tokens literais .

Esses tokens permitem um pouco de simpli cação nas Expressões Padrão. Eles funcionam exatamente da mesma forma que
os literais de texto ( '...' ), mas permitem apenas letras ( A-Z e a-z ), números ( 0-9 ), colchetes ( [ e ] ), pontos ( . ),
hífens ( - ) e sublinhados ( _ ). Portanto, não há espaços em branco, vírgulas etc.

A parte legal? Os tokens não precisam de aspas. Para que possamos fazer isso:

<div th:class="content">...</div>

ao invés de:

<div th:class="'content'">...</div>

4.7 Anexando textos

Os textos, independentemente de serem literais ou o resultado da avaliação de expressões de variáveis ou mensagens,


podem ser facilmente anexados usando o + operador:

<span th:text="'The name of the user is ' + ${user.name}">


https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 21/94
01/08/2020 Tutorial: Usando o Thymeleaf

4.8 Substituições literais

As substituições literais permitem uma formatação fácil de strings contendo valores de variáveis sem a necessidade de
anexar literais '...' + '...' .

Essas substituições devem ser cercadas por barras verticais ( | ), como:

<span th:text="|Welcome to our application, ${user.name}!|">

O que equivale a:

<span th:text="'Welcome to our application, ' + ${user.name} + '!'">

As substituições literais podem ser combinadas com outros tipos de expressões:

<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">

Somente expressões variável / mensagem ( ${...} , *{...} , #{...} ) são permitidos dentro |...| substituições
literais. Nenhum outro literal ( '...' ), tokens booleanos / numéricos, expressões condicionais etc. são.

4.9 Operações aritméticas

Algumas operações aritméticas também estão disponíveis: + , - , * , / e % .

<div th:with="isEven=(${prodStat.count} % 2 == 0)">

Observe que esses operadores também podem ser aplicados dentro das próprias expressões variáveis OGNL (e nesse caso
serão executados pelo OGNL em vez do mecanismo Thymeleaf Standard Expression):

<div th:with="isEven=${prodStat.count % 2 == 0}">

Observe que existem aliases textuais para alguns desses operadores: div ( / ), mod ( % ).

4.10 Comparadores e igualdade

Valores em expressões podem ser comparados com os > , < , >= e <= símbolos, e os == e != os operadores podem ser
usados para veri car a igualdade (ou a falta dela). Observe que o XML estabelece que os símbolos < e > não devem ser
usados em valores de atributo e, portanto, devem ser substituídos por &lt; e &gt; .

<div th:if="${prodStat.count} &gt; 1">


<span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">

Uma alternativa mais simples pode estar usando aliases de texto existentes para alguns desses operadores: gt ( > ), lt ( < ),
ge ( >= ), le ( <= ), not ( ! ). Também eq ( == ), neq / ne ( != ).

4.11 Expressões condicionais


https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 22/94
01/08/2020 Tutorial: Usando o Thymeleaf

Expressões condicionais destinam-se a avaliar apenas uma das duas expressões, dependendo do resultado da avaliação de
uma condição (que é outra expressão).

Vamos ter um olhar para um fragmento exemplo (a introdução de um outro atributo modi cador , th:class ):

<tr th:class="${row.even}? 'even' : 'odd'">


...
</tr>

Todas as três partes de uma expressão condicional ( condition , then e else ) são elas mesmas expressões, o que signi ca
que podem ser variáveis ( ${...} , *{...} ), mensagens ( #{...} ), URLs ( @{...} ) ou literais ( '...' ).

Expressões condicionais também podem ser aninhadas usando parênteses:

<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">


...
</tr>

Outras expressões também podem ser omitidas; nesse caso, um valor nulo será retornado se a condição for falsa:

<tr th:class="${row.even}? 'alt'">


...
</tr>

4.12 Expressões padrão (operador Elvis)

Uma expressão padrão é um tipo especial de valor condicional sem uma parte then . É equivalente ao operador Elvis presente
em alguns idiomas, como o Groovy, permite especi car duas expressões: a primeira é usada se não for avaliada como nula,
mas se o zer, a segunda será usada.

Vamos vê-lo em ação em nossa página de per l de usuário:

<div th:object="${session.user}">
...
<p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p>
</div>

Como você pode ver, o operador é ?: e nós o usamos aqui para especi car um valor padrão para um nome (neste caso, um
valor literal) apenas se o resultado da avaliação *{age} for nulo. Isto é, portanto, equivalente a:

<p>Age: <span th:text="*{age != null}? *{age} : '(no age specified)'">27</span>.</p>

Assim como os valores condicionais, eles podem conter expressões aninhadas entre parênteses:

<p>
Name:
<span th:text="*{firstName}?: (*{admin}? 'Admin' : #{default.username})">Sebastian</span>
</p>

4.13 O token de não operação

O token No-Operation é representado por um símbolo de sublinhado ( _ ).

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 23/94
01/08/2020 Tutorial: Usando o Thymeleaf

A idéia por trás desse token é especi car que o resultado desejado para uma expressão é não fazer nada , ou seja, fazer
exatamente como se o atributo processável (por exemplo th:text ) não estivesse presente.

Entre outras possibilidades, isso permite que os desenvolvedores usem prototipagem de texto como valores padrão. Por
exemplo, em vez de:

<span th:text="${user.name} ?: 'no user authenticated'">...</span>

… Podemos usar diretamente 'nenhum usuário autenticado' como um texto de prototipagem, o que resulta em um código
mais conciso e versátil do ponto de vista do design:

<span th:text="${user.name} ?: _">no user authenticated</span>

4.14 Conversão / formatação de dados

O Thymeleaf de ne uma sintaxe de colchete duplo para expressões de variável ( ${...} ) e selection ( *{...} ) que nos
permitem aplicar a conversão de dados por meio de um serviço de conversão con gurado .

Basicamente, é assim:

<td th:text="${{user.lastAccessDate}}">...</td>

Percebeu a cinta dupla lá ?: ${{...}} . Isso instrui o Thymeleaf a passar o resultado da user.lastAccessDate expressão para o
serviço de conversão e solicita que ele execute uma operação de formatação (uma conversão para String ) antes de gravar o
resultado.

Supondo que user.lastAccessDate seja do tipo java.util.Calendar , se um serviço de conversão (implementação de


IStandardConversionService ) tiver sido registrado e contiver uma conversão válida Calendar -> String , ele será aplicado.

A implementação padrão de IStandardConversionService (a StandardConversionService classe) simplesmente é executada


.toString() em qualquer objeto convertido em String . Para obter mais informações sobre como registrar uma
implementação de serviço de conversão personalizada , consulte a seção Mais sobre con guração .

Pacotes de integração do thymeleaf-spring3 o cial e thymeleaf-spring4 integrar de forma transparente mecanismo de


serviço de conversão de Thymeleaf com próprio Primavera Serviço de conversão de infra-estrutura, de modo que os
serviços de conversão e formatadores declarado na con guração do Spring será feita automaticamente disponível
para ${{...}} e *{{...}} expressões.

4.15 Pré-processamento

Além de todos esses recursos para o processamento de expressões, o Thymeleaf possui o recurso de pré-processamento de
expressões.

O pré-processamento é uma execução das expressões feitas antes da normal que permite a modi cação da expressão que
eventualmente será executada.

Expressões pré-processadas são exatamente iguais às normais, mas aparecem cercadas por um símbolo de sublinhado duplo
(como __${expression}__ ).

Vamos imaginar que temos uma Messages_fr.properties entrada i18n contendo uma expressão OGNL chamando um
método estático especí co da linguagem, como:

article.text=@myapp.translator.Translator@translateToFrench({0})

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 24/94
01/08/2020 Tutorial: Usando o Thymeleaf

… E um Messages_es.properties equivalent :

article.text=@myapp.translator.Translator@translateToSpanish({0})

Podemos criar um fragmento de marcação que avalie uma expressão ou outra, dependendo da localidade. Para isso, primeiro
selecionaremos a expressão (por pré-processamento) e, em seguida, deixaremos que o Thymeleaf a execute:

<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>

Observe que a etapa de pré-processamento para um código de idioma francês criará o seguinte equivalente:

<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>

A cadeia de pré-processamento __ pode ser escapada nos atributos usando \_\_ .

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 25/94
01/08/2020 Tutorial: Usando o Thymeleaf

5 De nindo valores de atributo


Este capítulo explicará como podemos de nir (ou modi car) valores de atributos em nossa marcação.

5.1 De nindo o valor de qualquer atributo

Digamos que nosso site publique um boletim informativo e desejamos que nossos usuários possam se inscrever nele, por isso
criamos um /WEB-INF/templates/subscribe.html modelo com um formulário:

<form action="subscribe.html">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" />
</fieldset>
</form>

Assim como no Thymeleaf, esse modelo começa mais como um protótipo estático do que para um aplicativo da web.
Primeiro, o action atributo em nosso formulário vincula estaticamente ao próprio arquivo de modelo, para que não haja
lugar para reescrever URLs úteis. Segundo, o value atributo no botão enviar faz com que seja exibido um texto em inglês,
mas gostaríamos que ele fosse internacionalizado.

Digite o th:attr atributo e sua capacidade de alterar o valor dos atributos das tags nas quais está de nido:

<form action="subscribe.html" th:attr="action=@{/subscribe}">


<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
</fieldset>
</form>

O conceito é bastante direto: th:attr simplesmente pega uma expressão que atribui um valor a um atributo. Depois de criar
o controlador e os arquivos de mensagens correspondentes, o resultado do processamento desse arquivo será:

<form action="/gtvg/subscribe">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="¡Suscríbe!"/>
</fieldset>
</form>

Além dos novos valores de atributo, também é possível ver que o nome do contexto do aplicativo foi pre xado
automaticamente na base da URL /gtvg/subscribe , conforme explicado no capítulo anterior.

Mas e se quiséssemos de nir mais de um atributo por vez? As regras XML não permitem que você de na um atributo duas
vezes em uma tag; portanto, th:attr você terá uma lista de atribuições separadas por vírgula, como:

<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

Dados os arquivos de mensagens necessários, isso resultará em:

<img src="/gtgv/images/gtvglogo.png" title="Logo de Good Thymes" alt="Logo de Good Thymes" />

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 26/94
01/08/2020 Tutorial: Usando o Thymeleaf

5.2 Con gurando valor para atributos especí cos

Até agora, você deve estar pensando que algo como:

<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>

… É uma parte bastante feia de marcação. A especi cação de uma atribuição dentro do valor de um atributo pode ser muito
prática, mas não é a maneira mais elegante de criar modelos se você precisar fazer isso o tempo todo.

Thymeleaf concorda com você, e é por isso que th:attr di cilmente é usado em modelos. Normalmente, você usará outros
th:* atributos cuja tarefa é de nir atributos de tag especí cos (e não apenas qualquer atributo como th:attr ).

Por exemplo, para de nir o value atributo, use th:value :

<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>

Isso parece muito melhor! Vamos tentar fazer o mesmo com o action atributo na form tag:

<form action="subscribe.html" th:action="@{/subscribe}">

E você se lembra daqueles th:href que colocamos em nosso home.html antes? Eles são exatamente esse mesmo tipo de
atributos:

<li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li>

Existem muitos atributos como estes, cada um deles direcionado a um atributo HTML5 especí co:

th:abbr th:accept th:accept-charset


th:accesskey th:action th:align

th:alt th:archive th:audio

th:autocomplete th:axis th:background

th:bgcolor th:border th:cellpadding


th:cellspacing th:challenge th:charset

th:cite th:class th:classid

th:codebase th:codetype th:cols

th:colspan th:compact th:content


th:contenteditable th:contextmenu th:data

th:datetime th:dir th:draggable

th:dropzone th:enctype th:for

th:form th:formaction th:formenctype


th:formmethod th:formtarget th:fragment

th:frame th:frameborder th:headers

th:height th:high th:href

th:hreflang th:hspace th:http-equiv


th:icon th:id th:inline

th:keytype th:kind th:label

th:lang th:list th:longdesc

th:low th:manifest th:marginheight

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 27/94
01/08/2020 Tutorial: Usando o Thymeleaf

th:marginwidth th:max th:maxlength

th:media th:method th:min

th:name th:onabort th:onafterprint


th:onbeforeprint th:onbeforeunload th:onblur

th:oncanplay th:oncanplaythrough th:onchange

th:onclick th:oncontextmenu th:ondblclick

th:ondrag th:ondragend th:ondragenter


th:ondragleave th:ondragover th:ondragstart

th:ondrop th:ondurationchange th:onemptied

th:onended th:onerror th:onfocus

th:onformchange th:onforminput th:onhashchange


th:oninput th:oninvalid th:onkeydown

th:onkeypress th:onkeyup th:onload

th:onloadeddata th:onloadedmetadata th:onloadstart

th:onmessage th:onmousedown th:onmousemove


th:onmouseout th:onmouseover th:onmouseup

th:onmousewheel th:onoffline th:ononline

th:onpause th:onplay th:onplaying

th:onpopstate th:onprogress th:onratechange


th:onreadystatechange th:onredo th:onreset

th:onresize th:onscroll th:onseeked

th:onseeking th:onselect th:onshow

th:onstalled th:onstorage th:onsubmit


th:onsuspend th:ontimeupdate th:onundo

th:onunload th:onvolumechange th:onwaiting

th:optimum th:pattern th:placeholder

th:poster th:preload th:radiogroup


th:rel th:rev th:rows

th:rowspan th:rules th:sandbox

th:scheme th:scope th:scrolling


th:size th:sizes th:span

th:spellcheck th:src th:srclang

th:standby th:start th:step

th:style th:summary th:tabindex


th:target th:title th:type

th:usemap th:value th:valuetype

th:vspace th:width th:wrap

th:xmlbase th:xmllang th:xmlspace

5.3 De nindo mais de um valor por vez

Existem dois atributos bastante especiais chamados th:alt-title e th:lang-xmllang que podem ser usados para de nir
dois atributos com o mesmo valor ao mesmo tempo. Especi camente:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 28/94
01/08/2020 Tutorial: Usando o Thymeleaf

th:alt-title irá de nir alt e title .


th:lang-xmllang irá de nir lang e xml:lang .

Para nossa home page do GTVG, isso nos permitirá substituir:

<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />

… Ou isto, que é equivalente:

<img src="../../images/gtvglogo.png"
th:src="@{/images/gtvglogo.png}" th:title="#{logo}" th:alt="#{logo}" />

…com isso:

<img src="../../images/gtvglogo.png"
th:src="@{/images/gtvglogo.png}" th:alt-title="#{logo}" />

5.4 Anexando e anexando

O Thymeleaf também oferece os atributos th:attrappend e th:attrprepend , que acrescentam (su xo) ou precedem
(pre xo) o resultado de sua avaliação aos valores de atributo existentes.

Por exemplo, convém armazenar o nome de uma classe CSS a ser adicionada (não con gurada, apenas adicionada) a um de
seus botões em uma variável de contexto, porque a classe CSS especí ca a ser usada dependeria de algo que o usuário fez
antes:

<input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />

Se você processar este modelo com a cssStyle variável de nida como "warning" , obterá:

<input type="button" value="Do it!" class="btn warning" />

Há também dois atributos especí cos anexos no Dialeto Padrão: os atributos th:classappend e th:styleappend , que são
usados para adicionar uma classe CSS ou um fragmento de estilo a um elemento sem substituir os existentes:

<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">

(Não se preocupe com esse th:each atributo. É um atributo de iteração e falaremos sobre isso mais tarde.)

5.5 Atributos booleanos de valor xo

O HTML tem o conceito de atributos booleanos , atributos que não têm valor e a presença de um signi ca que o valor é
"verdadeiro". No XHTML, esses atributos assumem apenas 1 valor, que é ele próprio.

Por exemplo checked :

<input type="checkbox" name="option2" checked /> <!-- HTML -->


<input type="checkbox" name="option1" checked="checked" /> <!-- XHTML -->

O Dialeto Padrão inclui atributos que permitem de nir esses atributos avaliando uma condição, de modo que, se avaliado
como verdadeiro, o atributo será de nido como seu valor xo e, se avaliado como falso, o atributo não será de nido:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 29/94
01/08/2020 Tutorial: Usando o Thymeleaf

<input type="checkbox" name="active" th:checked="${user.active}" />

Os seguintes atributos booleanos de valor xo existem no Dialeto Padrão:

th:async th:autofocus th:autoplay

th:checked th:controls th:declare

th:default th:defer th:disabled


th:formnovalidate th:hidden th:ismap

th:loop th:multiple th:novalidate

th:nowrap th:open th:pubdate


th:readonly th:required th:reversed

th:scoped th:seamless th:selected

5.6 Con gurando o valor de qualquer atributo (processador de atributo padrão)

O Thymeleaf oferece um processador de atributos padrão que nos permite de nir o valor de qualquer atributo, mesmo que
nenhum th:* processador especí co tenha sido de nido para ele no Dialeto Padrão.

Então, algo como:

<span th:whatever="${user.name}">...</span>

Vai resultar em:

<span whatever="John Apricot">...</span>

5.7 Suporte para nomes de atributo e elemento compatíveis com HTML5

Também é possível usar uma sintaxe completamente diferente para aplicar processadores aos seus modelos de uma maneira
mais amigável ao HTML5.

<table>
<tr data-th-each="user : ${users}">
<td data-th-text="${user.login}">...</td>
<td data-th-text="${user.name}">...</td>
</tr>
</table>

A data-{prefix}-{name} sintaxe é a maneira padrão de escrever atributos personalizados em HTML5, sem exigir que os
desenvolvedores usem nomes como namespace th:* . O Thymeleaf torna essa sintaxe disponível automaticamente para
todos os seus dialetos (não apenas os padrão).

Há também uma sintaxe para especi car tags personalizadas:, {prefix}-{name} que seguem a especi cação de Elementos
Personalizados do W3C (parte da especi cação maior dos Componentes da Web do W3C ). Isso pode ser usado, por exemplo,
para o th:block elemento (ou também th-block ), que será explicado em uma seção posterior.

Importante: esta sintaxe é uma adição ao namespace th:* , não o substitui. Não há intenção de descontinuar a sintaxe no
espaço para nome no futuro.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 30/94
01/08/2020 Tutorial: Usando o Thymeleaf

6 Iteração
Até agora, criamos uma página inicial, uma página de per l de usuário e também uma página para permitir que os usuários se
inscrevam em nossa newsletter ... mas e nossos produtos? Para isso, precisaremos de uma maneira de percorrer os itens de
uma coleção para criar nossa página de produtos.

6.1 Noções básicas de iteração

Para exibir produtos em nossa /WEB-INF/templates/product/list.html página, usaremos uma tabela. Cada um de nossos
produtos será exibido em uma linha (um <tr> elemento) e, portanto, para o nosso modelo, precisaremos criar uma linha de
modelo - uma que exempli que como queremos que cada produto seja exibido - e instrua o Thymeleaf a repeti-lo, uma vez
para cada produto.

A língua padrão nos oferece um atributo para exatamente isso: th:each .

Usando th: each

Para nossa página de lista de produtos, precisaremos de um método de controlador que recupere a lista de produtos da
camada de serviço e a adicione ao contexto do modelo:

public void process(


final HttpServletRequest request, final HttpServletResponse response,
final ServletContext servletContext, final ITemplateEngine templateEngine)
throws Exception {

ProductService productService = new ProductService();


List<Product> allProducts = productService.findAll();

WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());


ctx.setVariable("prods", allProducts);

templateEngine.process("product/list", ctx, response.getWriter());

E então usaremos th:each em nosso modelo para percorrer a lista de produtos:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<body>

<h1>Product list</h1>

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 31/94
01/08/2020 Tutorial: Usando o Thymeleaf
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>

<p>
<a href="../home.html" th:href="@{/}">Return to home</a>
</p>

</body>

</html>

Esse prod : ${prods} valor de atributo que você vê acima signi ca "para cada elemento no resultado da avaliação ${prods} ,
repita esse fragmento de modelo, usando o elemento atual em uma variável chamada prod". Vamos dar um nome para cada
uma das coisas que vemos:

Vamos chamar ${prods} a expressão iterada ou variável iterada .


Vamos chamar prod a variável de iteração ou simplesmente a variável de iter .

Observe que a prod variável iter tem o escopo de nido para o <tr> elemento, o que signi ca que está disponível para tags
internas como <td> .

Valores iteráveis

A java.util.List classe não é o único valor que pode ser usado para iteração no Thymeleaf. Existe um conjunto bastante
completo de objetos que são considerados iteráveis por um th:each atributo:

Qualquer objeto implementado java.util.Iterable


Qualquer objeto implementado java.util.Enumeration .
Qualquer objeto implementado java.util.Iterator , cujos valores serão usados conforme retornados pelo iterador,
sem a necessidade de armazenar em cache todos os valores na memória.
Qualquer objeto implementado java.util.Map . Ao iterar mapas, as variáveis iter serão de classe java.util.Map.Entry .
Qualquer matriz.
Qualquer outro objeto será tratado como se fosse uma lista de valor único contendo o próprio objeto.

6.2 Mantendo o status da iteração

Ao usar th:each , o Thymeleaf oferece um mecanismo útil para acompanhar o status da sua iteração: a variável status .

As variáveis de status são de nidas em um th:each atributo e contêm os seguintes dados:

O índice de iteração atual , começando com 0. Essa é a index propriedade


O índice de iteração atual , começando com 1. Essa é a count propriedade.
A quantidade total de elementos na variável iterada. Esta é a size propriedade.
A variável iter para cada iteração. Esta é a current propriedade.
Se a iteração atual é par ou ímpar. Essas são as even/odd propriedades booleanas.
Se a iteração atual é a primeira. Esta é a first propriedade booleana.
Se a iteração atual é a última. Esta é a last propriedade booleana.

Vamos ver como poderíamos usá-lo com o exemplo anterior:

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 32/94
01/08/2020 Tutorial: Usando o Thymeleaf
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>

A variável status ( iterStat neste exemplo) é de nida no th:each atributo, escrevendo seu nome após a própria variável iter,
separada por vírgula. Assim como a variável iter, a variável status também tem o escopo de nido no fragmento de código
de nido pela tag que contém o th:each atributo.

Vamos dar uma olhada no resultado do processamento do nosso modelo:

<!DOCTYPE html>

<html>

<head>
<title>Good Thymes Virtual Grocery</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
</head>

<body>

<h1>Product list</h1>

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr class="odd">
<td>Fresh Sweet Basil</td>
<td>4.99</td>
<td>yes</td>
</tr>
<tr>
<td>Italian Tomato</td>
<td>1.25</td>
<td>no</td>
</tr>
<tr class="odd">
<td>Yellow Bell Pepper</td>
<td>2.50</td>
<td>yes</td>
</tr>
<tr>
<td>Old Cheddar</td>
<td>18.75</td>
<td>yes</td>
</tr>
</table>

<p>
<a href="/gtvg/" shape="rect">Return to home</a>
</p>

</body>

</html>

Observe que nossa variável de status de iteração funcionou perfeitamente, estabelecendo a odd classe CSS apenas para
linhas ímpares.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 33/94
01/08/2020 Tutorial: Usando o Thymeleaf

Se você não de nir explicitamente uma variável de status, o Thymeleaf sempre criará uma para você com o su xo Stat do
nome da variável de iteração:

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
</table>

6.3 Otimizando através da recuperação lenta de dados

Às vezes, podemos otimizar a recuperação de coleções de dados (por exemplo, de um banco de dados) para que essas
coleções sejam recuperadas apenas se forem realmente usadas.

Na verdade, isso é algo que pode ser aplicado a qualquer parte dos dados, mas, dado o tamanho que as coleções na
memória podem ter, recuperar as coleções que devem ser iteradas é o caso mais comum desse cenário.

Para suportar isso, o Thymeleaf oferece um mecanismo para carregar lentamente variáveis de contexto . As variáveis de
contexto que implementam a ILazyContextVariable interface - provavelmente estendendo sua
LazyContextVariable implementação padrão - serão resolvidas no momento da execução. Por exemplo:

context.setVariable(
"users",
new LazyContextVariable<List<User>>() {
@Override
protected List<User> loadValue() {
return databaseRepository.findAllUsers();
}
});

Essa variável pode ser usada sem o conhecimento de sua preguiça , em códigos como:

<ul>
<li th:each="u : ${users}" th:text="${u.name}">user name</li>
</ul>

Mas, ao mesmo tempo, nunca será inicializado (seu loadValue() método nunca será chamado) se for condition avaliado
false em código como:

<ul th:if="${condition}">
<li th:each="u : ${users}" th:text="${u.name}">user name</li>
</ul>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 34/94
01/08/2020 Tutorial: Usando o Thymeleaf

7 Avaliação Condicional

7.1 Condicionais simples: "se" e "a menos"

Às vezes, você precisará de um fragmento do seu modelo para aparecer apenas no resultado se uma determinada condição
for atendida.

Por exemplo, imagine que queremos mostrar em nossa tabela de produtos uma coluna com o número de comentários que
existem para cada produto e, se houver algum comentário, um link para a página de detalhes do comentário para esse
produto.

Para fazer isso, usaríamos o th:if atributo:

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span> comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
</table>

Muitas coisas para ver aqui, então vamos nos concentrar na linha importante:

<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>

Isso criará um link para a página de comentários (com URL /product/comments ) com um prodId parâmetro de nido como o
id do produto, mas apenas se o produto tiver algum comentário.

Vamos dar uma olhada na marcação resultante:

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr>
<td>Fresh Sweet Basil</td>
<td>4.99</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 35/94
01/08/2020 Tutorial: Usando o Thymeleaf
<tr class="odd">
<td>Italian Tomato</td>
<td>1.25</td>
<td>no</td>
<td>
<span>2</span> comment/s
<a href="/gtvg/product/comments?prodId=2">view</a>
</td>
</tr>
<tr>
<td>Yellow Bell Pepper</td>
<td>2.50</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr class="odd">
<td>Old Cheddar</td>
<td>18.75</td>
<td>yes</td>
<td>
<span>1</span> comment/s
<a href="/gtvg/product/comments?prodId=4">view</a>
</td>
</tr>
</table>

Perfeito! Era exatamente o que queríamos.

Observe que o th:if atributo não apenas avaliará as condições booleanas . Seus recursos vão um pouco além disso e avaliará
a expressão especi cada da true seguinte maneira:

Se o valor não for nulo:

Se o valor é um booleano e é true .


Se o valor for um número e for diferente de zero
Se o valor for um caractere e for diferente de zero
Se o valor for uma String e não for "false", "o " ou "no"
Se o valor não for um booleano, um número, um caractere ou uma String.

(Se o valor for nulo, th: if será avaliado como falso).

Além disso, th:if possui um atributo inverso th:unless , que poderíamos ter usado no exemplo anterior, em vez de usar um
not dentro da expressão OGNL:

<a href="comments.html"
th:href="@{/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>

7.2 Instruções de troca

Também existe uma maneira de exibir o conteúdo condicionalmente usando o equivalente a uma estrutura de comutador em
Java: o conjunto th:switch / th:case atributo.

<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
</div>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 36/94
01/08/2020 Tutorial: Usando o Thymeleaf

Observe que, assim que um th:case atributo é avaliado como true , todos os outros th:case atributos no mesmo contexto
de switch são avaliados como false .

A opção padrão é especi cada como th:case="*" :

<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 37/94
01/08/2020 Tutorial: Usando o Thymeleaf

8 Layout do modelo

8.1 Incluindo fragmentos de modelo

De nindo e referenciando fragmentos

Nos nossos modelos, geralmente queremos incluir partes de outros modelos, partes como rodapés, cabeçalhos, menus ...

Para fazer isso, o Thymeleaf precisa que de namos essas partes, “fragmentos”, para inclusão, o que pode ser feito usando o
th:fragment atributo

Digamos que desejemos adicionar um rodapé de direitos autorais padrão a todas as nossas páginas de compras, por isso
criamos um /WEB-INF/templates/footer.html arquivo que contém este código:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<body>

<div th:fragment="copy">
&copy; 2011 The Good Thymes Virtual Grocery
</div>

</body>

</html>

O código acima de ne um fragmento chamado copy que podemos incluir facilmente em nossa página inicial usando um dos
atributos th:insert ou th:replace (e também th:include , embora seu uso não seja mais recomendado desde o Thymeleaf
3.0):

<body>

...

<div th:insert="~{footer :: copy}"></div>

</body>

Observe que th:insert espera uma expressão de fragmento ( ~{...} ), que é uma expressão que resulta em um fragmento . No
exemplo acima, porém, que é uma expressão de fragmento não complexa , o anexo ( ~{ , } ) é completamente opcional,
portanto o código acima seria equivalente a:

<body>

...

<div th:insert="footer :: copy"></div>

</body>

Sintaxe de especi cação de fragmento

A sintaxe das expressões de fragmento é bastante direta. Existem três formatos diferentes:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 38/94
01/08/2020 Tutorial: Usando o Thymeleaf

"~{templatename::selector}" Inclui o fragmento resultante da aplicação do seletor de marcação especi cado no


modelo nomeado templatename . Observe que selector pode ser um mero nome de fragmento; portanto, você pode
especi car algo tão simples quanto ~{templatename::fragmentname} o ~{footer :: copy} descrito acima.

A sintaxe do Markup Selector é de nida pela biblioteca de análise AttoParser subjacente e é semelhante às
expressões XPath ou seletores CSS. Veja o Apêndice C para mais informações.

"~{templatename}" Inclui o modelo completo nomeado templatename .

Observe que o nome do modelo usado em th:insert / th:replace tags terá que ser resolvido pelo Resolvedor de
modelos atualmente sendo usado pelo Mecanismo de modelos.

~{::selector}" ou "~{this::selector}" Insere um fragmento do mesmo modelo, correspondente selector . Se não for
encontrado no modelo em que a expressão aparece, a pilha de chamadas de modelo (inserções) é percorrida em direção
ao modelo originalmente processado (a raiz ), até selector corresponder em algum nível.

Ambos templatename e selector nos exemplos acima podem ser expressões com recursos completos (mesmo condicionais!)
Como:

<div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>

Observe novamente como o ~{...} envelope ao redor é opcional em th:insert / th:replace .

Fragmentos podem incluir qualquer th:* atributo. Esses atributos serão avaliados assim que o fragmento for incluído no
modelo de destino (aquele com o atributo th:insert / th:replace ) e poderão fazer referência a quaisquer variáveis de
contexto de nidas nesse modelo de destino.

Uma grande vantagem dessa abordagem aos fragmentos é que você pode gravar seus fragmentos em páginas
perfeitamente exibidas por um navegador, com uma estrutura de marcação completa e até válida , mantendo a
capacidade de fazer com que o Thymeleaf os inclua em outros modelos.

Referenciando fragmentos sem th:fragment

Graças ao poder dos seletores de marcação, podemos incluir fragmentos que não usam th:fragment atributos. Pode até ser
um código de marcação vindo de um aplicativo diferente sem o conhecimento do Thymeleaf:

...
<div id="copy-section">
&copy; 2011 The Good Thymes Virtual Grocery
</div>
...

Podemos usar o fragmento acima simplesmente referenciando-o por seu id atributo, de maneira semelhante a um seletor
CSS:

<body>

...

<div th:insert="~{footer :: #copy-section}"></div>

</body>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 39/94
01/08/2020 Tutorial: Usando o Thymeleaf

Diferença entre th:inserte th:replace(e th:include)

E qual é a diferença entre th:insert e th:replace (e th:include , não recomendado desde o 3.0)?

th:insert é o mais simples: ele simplesmente insere o fragmento especi cado como o corpo da tag do host.

th:replace na verdade, substitui sua tag de host pelo fragmento especi cado.

th:include é semelhante a th:insert , mas, em vez de inserir o fragmento, ele apenas insere o conteúdo desse
fragmento.

Portanto, um fragmento HTML como este:

<footer th:fragment="copy">
&copy; 2011 The Good Thymes Virtual Grocery
</footer>

… Incluído três vezes em <div> tags de host , assim:

<body>

...

<div th:insert="footer :: copy"></div>

<div th:replace="footer :: copy"></div>

<div th:include="footer :: copy"></div>

</body>

…vai resultar em:

<body>

...

<div>
<footer>
&copy; 2011 The Good Thymes Virtual Grocery
</footer>
</div>

<footer>
&copy; 2011 The Good Thymes Virtual Grocery
</footer>

<div>
&copy; 2011 The Good Thymes Virtual Grocery
</div>

</body>

8.2 Assinaturas de fragmentos parametrizáveis

Para criar um mecanismo mais parecido com uma função para fragmentos de modelo, os fragmentos de nidos com
th:fragment podem especi car um conjunto de parâmetros:

<div th:fragment="frag (onevar,twovar)">


<p th:text="${onevar} + ' - ' + ${twovar}">...</p>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 40/94
01/08/2020 Tutorial: Usando o Thymeleaf
</div>

Isso requer o uso de uma dessas duas sintaxes para chamar o fragmento de th:insert ou th:replace :

<div th:replace="::frag (${value1},${value2})">...</div>


<div th:replace="::frag (onevar=${value1},twovar=${value2})">...</div>

Observe que o pedido não é importante na última opção:

<div th:replace="::frag (twovar=${value2},onevar=${value1})">...</div>

Fragmentar variáveis locais sem argumentos de fragmento

Mesmo que fragmentos sejam de nidos sem argumentos como este:

<div th:fragment="frag">
...
</div>

Poderíamos usar a segunda sintaxe especi cada acima para chamá-los (e somente a segunda):

<div th:replace="::frag (onevar=${value1},twovar=${value2})">

Isso seria equivalente a uma combinação de th:replace e th:with :

<div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">

Observe que essa especi cação de variáveis locais para um fragmento - independentemente de ter ou não uma assinatura de
argumento - não faz com que o contexto seja esvaziado antes de sua execução. Os fragmentos ainda poderão acessar todas
as variáveis de contexto que estão sendo usadas no modelo de chamada como elas são atualmente.

th: a rmar para asserções no modelo

O th:assert atributo pode especi car uma lista de expressões separadas por vírgula que deve ser avaliada e verdadeira para
cada avaliação, gerando uma exceção, se não.

<div th:assert="${onevar},(${twovar} != 43)">...</div>

Isso é útil para validar parâmetros em uma assinatura de fragmento:

<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>

8.3 Layouts exíveis: além da mera inserção de fragmentos

Graças às expressões de fragmento , podemos especi car parâmetros para fragmentos que não são textos, números, objetos
de bean ... mas fragmentos de marcação.

Isso nos permite criar nossos fragmentos de forma que possam ser enriquecidos com a marcação proveniente dos modelos de
chamada, resultando em um mecanismo de layout de modelo muito exível .

Observe o uso das variáveis title e links no fragmento abaixo:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 41/94
01/08/2020 Tutorial: Usando o Thymeleaf

<head th:fragment="common_header(title,links)">

<title th:replace="${title}">The awesome application</title>

<!-- Common styles and scripts -->


<link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}">
<link rel="shortcut icon" th:href="@{/images/favicon.ico}">
<script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script>

<!--/* Per-page placeholder for additional links */-->


<th:block th:replace="${links}" />

</head>

Agora podemos chamar esse fragmento como:

...
<head th:replace="base :: common_header(~{::title},~{::link})">

<title>Awesome - Main</title>

<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">


<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">

</head>
...

… E o resultado usará as tags reais <title> e <link> do nosso modelo de chamada como os valores das variáveis title e
links , resultando em nosso fragmento sendo personalizado durante a inserção:

...
<head>

<title>Awesome - Main</title>

<!-- Common styles and scripts -->


<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>

<link rel="stylesheet" href="/awe/css/bootstrap.min.css">


<link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">

</head>
...

Usando o fragmento vazio

Uma expressão de fragmento especial, o fragmento vazio ( ~{} ), pode ser usada para especi car nenhuma marcação . Usando
o exemplo anterior:

<head th:replace="base :: common_header(~{::title},~{})">

<title>Awesome - Main</title>

</head>
...

Observe como o segundo parâmetro do fragmento ( links ) é de nido como fragmento vazio e, portanto, nada é escrito para
o <th:block th:replace="${links}" /> bloco:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 42/94
01/08/2020 Tutorial: Usando o Thymeleaf

...
<head>

<title>Awesome - Main</title>

<!-- Common styles and scripts -->


<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>

</head>
...

Usando o token sem operação

O no-op também pode ser usado como parâmetro para um fragmento, se quisermos apenas permitir que nosso fragmento
use sua marcação atual como um valor padrão. Novamente, usando o common_header exemplo:

...
<head th:replace="base :: common_header(_,~{::link})">

<title>Awesome - Main</title>

<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">


<link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}">

</head>
...

Veja como o title argumento (primeiro argumento do common_header fragmento) é de nido como no-op ( _ ), o que faz com
que esta parte do fragmento não seja executada ( title = = sem operação ):

<title th:replace="${title}">The awesome application</title>

Então o resultado é:

...
<head>

<title>The awesome application</title>

<!-- Common styles and scripts -->


<link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css">
<link rel="shortcut icon" href="/awe/images/favicon.ico">
<script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script>

<link rel="stylesheet" href="/awe/css/bootstrap.min.css">


<link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css">

</head>
...

Inserção condicional avançada de fragmentos

A disponibilidade do fragmento vazio e do token sem operação nos permite realizar a inserção condicional de fragmentos de
uma maneira muito fácil e elegante.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 43/94
01/08/2020 Tutorial: Usando o Thymeleaf

Por exemplo, poderíamos fazer isso para inserir nosso common :: adminhead fragmento apenas se o usuário for um
administrador e não inserir nada (fragmento vazio) se não:

...
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>
...

Além disso, podemos usar o token de não operação para inserir um fragmento somente se a condição especi cada for
atendida, mas deixar a marcação sem modi cações se a condição não for atendida:

...
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : _">
Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support.
</div>
...

Além disso, se con guramos nossos resolvedores de modelos para veri car a existência dos recursos do modelo - por meio de
seu checkExistence sinalizador -, podemos usar a existência do próprio fragmento como condição em uma operação padrão :

...
<!-- The body of the <div> will be used if the "common :: salutation" fragment -->
<!-- does not exist (or is empty). -->
<div th:insert="~{common :: salutation} ?: _">
Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support.
</div>
...

8.4 Removendo fragmentos de modelo

De volta ao aplicativo de exemplo, vamos revisitar a última versão do nosso modelo de lista de produtos:

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span> comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
</table>

Esse código é excelente como modelo, mas como uma página estática (quando aberto diretamente por um navegador sem o
Thymeleaf processá-lo), não seria um bom protótipo.

Por quê? Porque, embora perfeitamente exibível pelos navegadores, essa tabela possui apenas uma linha e essa linha possui
dados simulados. Como protótipo, simplesmente não pareceria realista o su ciente ... deveríamos ter mais de um produto,
precisamos de mais linhas .

Então, vamos adicionar alguns:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 44/94
01/08/2020 Tutorial: Usando o Thymeleaf

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span> comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
<tr class="odd">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr>
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
</table>

Ok, agora temos três, de nitivamente melhores para um protótipo. Mas… o que acontecerá quando o processarmos com o
Thymeleaf ?:

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr>
<td>Fresh Sweet Basil</td>
<td>4.99</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr class="odd">
<td>Italian Tomato</td>
<td>1.25</td>
<td>no</td>
<td>
<span>2</span> comment/s
<a href="/gtvg/product/comments?prodId=2">view</a>
</td>
</tr>
<tr>
<td>Yellow Bell Pepper</td>
<td>2.50</td>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 45/94
01/08/2020 Tutorial: Usando o Thymeleaf
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr class="odd">
<td>Old Cheddar</td>
<td>18.75</td>
<td>yes</td>
<td>
<span>1</span> comment/s
<a href="/gtvg/product/comments?prodId=4">view</a>
</td>
</tr>
<tr class="odd">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr>
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
</table>

As duas últimas linhas são linhas simuladas! Bem, é claro que são: a iteração foi aplicada apenas à primeira linha; portanto,
não há razão para que o Thymeleaf tenha removido as outras duas.

Precisamos de uma maneira de remover essas duas linhas durante o processamento do modelo. Vamos usar o
th:remove atributo na segunda e terceira <tr> tags:

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span> comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
<tr class="odd" th:remove="all">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr th:remove="all">
<td>Mild Cinnamon</td>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 46/94
01/08/2020 Tutorial: Usando o Thymeleaf
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
</table>

Depois de processado, tudo parecerá novamente como deveria:

<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
<tr>
<td>Fresh Sweet Basil</td>
<td>4.99</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr class="odd">
<td>Italian Tomato</td>
<td>1.25</td>
<td>no</td>
<td>
<span>2</span> comment/s
<a href="/gtvg/product/comments?prodId=2">view</a>
</td>
</tr>
<tr>
<td>Yellow Bell Pepper</td>
<td>2.50</td>
<td>yes</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr class="odd">
<td>Old Cheddar</td>
<td>18.75</td>
<td>yes</td>
<td>
<span>1</span> comment/s
<a href="/gtvg/product/comments?prodId=4">view</a>
</td>
</tr>
</table>

E o que esse all valor no atributo signi ca? th:remove pode se comportar de cinco maneiras diferentes, dependendo do seu
valor:

all : Remova a tag que contém e todos os seus lhos.


body : Não remova a tag que contém, mas remova todos os seus lhos.
tag : Remova a tag que contém, mas não remova seus lhos.
all-but-first : Remova todos os lhos da tag que contém, exceto a primeira.
none : Fazer nada. Este valor é útil para avaliação dinâmica.

Para que esse all-but-first valor pode ser útil? Isso nos permitirá economizar um pouco th:remove="all" na criação de
protótipos:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 47/94
01/08/2020 Tutorial: Usando o Thymeleaf

<table>
<thead>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
<th>COMMENTS</th>
</tr>
</thead>
<tbody th:remove="all-but-first">
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
<td>
<span th:text="${#lists.size(prod.comments)}">2</span> comment/s
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>
</td>
</tr>
<tr class="odd">
<td>Blue Lettuce</td>
<td>9.55</td>
<td>no</td>
<td>
<span>0</span> comment/s
</td>
</tr>
<tr>
<td>Mild Cinnamon</td>
<td>1.99</td>
<td>yes</td>
<td>
<span>3</span> comment/s
<a href="comments.html">view</a>
</td>
</tr>
</tbody>
</table>

O th:remove atributo pode tomar qualquer Expressão Thymeleaf padrão , desde que ele retorna um dos valores permitidos de
corda ( all , tag , body , all-but-first ou none ).

Isso signi ca que as remoções podem ser condicionais, como:

<a href="/something" th:remove="${condition}? tag : none">Link text not to be removed</a>

Observe também que th:remove considera null um sinônimo para none , portanto, o seguinte funciona da mesma forma que
o exemplo acima:

<a href="/something" th:remove="${condition}? tag">Link text not to be removed</a>

Nesse caso, se ${condition} for falso, null será retornado e, portanto, nenhuma remoção será executada.

8.5 Herança de layout

Para poder ter um único arquivo como layout, fragmentos podem ser usados. Um exemplo de um layout simples com title e
content usando th:fragment e th:replace :

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 48/94
01/08/2020 Tutorial: Usando o Thymeleaf

<!DOCTYPE html>
<html th:fragment="layout (title, content)" xmlns:th="http://www.thymeleaf.org">
<head>
<title th:replace="${title}">Layout Title</title>
</head>
<body>
<h1>Layout H1</h1>
<div th:replace="${content}">
<p>Layout content</p>
</div>
<footer>
Layout footer
</footer>
</body>
</html>

Este exemplo declara um fragmento chamado layout com título e conteúdo como parâmetros. Ambos serão substituídos na
página que o herdou pelas expressões de fragmento fornecidas no exemplo abaixo.

<!DOCTYPE html>
<html th:replace="~{layoutFile :: layout(~{::title}, ~{::section})}">
<head>
<title>Page Title</title>
</head>
<body>
<section>
<p>Page content</p>
<div>Included on page</div>
</section>
</body>
</html>

Nesse arquivo, a html tag será substituída pelo layout , mas no layout title e content será substituída por title e
section blocos, respectivamente.

Se desejado, o layout pode ser composto por vários fragmentos como cabeçalho e rodapé .

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 49/94
01/08/2020 Tutorial: Usando o Thymeleaf

9 Variáveis Locais
O Thymeleaf chama variáveis locais de variáveis de nidas para um fragmento especí co de um modelo e estão disponíveis
apenas para avaliação dentro desse fragmento.

Um exemplo que já vimos é a prod variável iter em nossa página de lista de produtos:

<tr th:each="prod : ${prods}">


...
</tr>

Essa prod variável estará disponível apenas dentro dos limites da <tr> tag. Especi camente:

Ele estará disponível para quaisquer outros th:* atributos executados nessa tag com menos precedência do que
th:each (o que signi ca que eles serão executados depois th:each ).
Ele estará disponível para qualquer elemento lho da <tr> tag, como qualquer <td> elemento.

O Thymeleaf oferece uma maneira de declarar variáveis locais sem iteração, usando o th:with atributo, e sua sintaxe é
semelhante à das atribuições de valor de atributo:

<div th:with="firstPer=${persons[0]}">
<p>
The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.
</p>
</div>

Quando th:with é processada, essa firstPer variável é criada como uma variável local e adicionada ao mapa de variáveis
proveniente do contexto, para que esteja disponível para avaliação juntamente com outras variáveis declaradas no contexto,
mas apenas dentro dos limites da <div> marca que o contém .

Você pode de nir várias variáveis ao mesmo tempo usando a sintaxe usual de atribuição múltipla:

<div th:with="firstPer=${persons[0]},secondPer=${persons[1]}">
<p>
The name of the first person is <span th:text="${firstPer.name}">Julius Caesar</span>.
</p>
<p>
But the name of the second person is
<span th:text="${secondPer.name}">Marcus Antonius</span>.
</p>
</div>

O th:with atributo permite reutilizar variáveis de nidas no mesmo atributo:

<div th:with="company=${user.company + ' Co.'},account=${accounts[company]}">...</div>

Vamos usar isso na página inicial do nosso supermercado! Lembra do código que escrevemos para gerar uma data formatada?

<p>
Today is:
<span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 february 2011</span>
</p>

Bem, e se quiséssemos que "dd MMMM yyyy" realmente dependesse da localidade? Por exemplo, podemos adicionar a
seguinte mensagem ao nosso home_en.properties :

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 50/94
01/08/2020 Tutorial: Usando o Thymeleaf

date.format=MMMM dd'','' yyyy

… E um equivalente ao nosso home_es.properties :

date.format=dd ''de'' MMMM'','' yyyy

Agora, vamos usar th:with para obter o formato da data localizada em uma variável e usá-lo em nossa th:text expressão:

<p th:with="df=#{date.format}">
Today is: <span th:text="${#calendars.format(today,df)}">13 February 2011</span>
</p>

Isso foi limpo e fácil. De fato, considerando que o valor th:with é superior precedence a th:text , poderíamos ter resolvido
tudo isso na span tag:

<p>
Today is:
<span th:with="df=#{date.format}"
th:text="${#calendars.format(today,df)}">13 February 2011</span>
</p>

Você pode estar pensando: Precedência? Ainda não conversamos sobre isso! Bem, não se preocupe, porque é exatamente
disso que trata o próximo capítulo.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 51/94
01/08/2020 Tutorial: Usando o Thymeleaf

10 Precedência de Atributos
O que acontece quando você escreve mais de um th:* atributo na mesma tag? Por exemplo:

<ul>
<li th:each="item : ${items}" th:text="${item.description}">Item description here...</li>
</ul>

Esperamos que esse th:each atributo seja executado antes de th:text obter os resultados desejados, mas, considerando
que os padrões HTML / XML não dão nenhum signi cado à ordem em que os atributos em uma tag são gravados, uma
precedência mecanismo precisou ser estabelecido nos próprios atributos para garantir que isso funcionasse conforme o
esperado.

Portanto, todos os atributos do Thymeleaf de nem uma precedência numérica, que estabelece a ordem na qual eles são
executados na tag. Esta ordem é:

Ordem Característica Atributos

1 Inclusão de fragmentos th:insert


th:replace

2 Iteração de fragmento th:each

3 Avaliação condicional th:if


th:unless
th:switch
th:case

4 De nição de variável local th:object


th:with

5 Modi cação geral de atributo th:attr


th:attrprepend
th:attrappend

6 Modi cação de atributo especí co th:value


th:href
th:src
...

7 Texto (modi cação do corpo da tag) th:text


th:utext

8 Especi cação de fragmento th:fragment

9 Remoção de fragmentos th:remove

Esse mecanismo de precedência signi ca que o fragmento de iteração acima fornecerá exatamente os mesmos resultados se
a posição do atributo for invertida (embora seja um pouco menos legível):

<ul>
<li th:text="${item.description}" th:each="item : ${items}">Item description here...</li>
</ul>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 52/94
01/08/2020 Tutorial: Usando o Thymeleaf

11 Comentários e Blocos

11.1 Comentários HTML / XML padrão

Comentários padrão em HTML / XML <!-- ... --> podem ser usados em qualquer lugar dos modelos Thymeleaf. Qualquer
coisa dentro desses comentários não será processada pelo Thymeleaf e será copiada textualmente para o resultado:

<!-- User info follows -->


<div th:text="${...}">
...
</div>

11.2 Blocos de comentário no nível do analisador Thymeleaf

Os blocos de comentários no nível do analisador são códigos que serão simplesmente removidos do modelo quando o
Thymeleaf o analisar. Eles se parecem com isso:

<!--/* This code will be removed at Thymeleaf parsing time! */-->

O Thymeleaf removerá tudo entre <!--/* e */--> , portanto, esses blocos de comentários também podem ser usados para
exibir código quando um modelo estiver estaticamente aberto, sabendo que ele será removido quando o Thymeleaf o
processar:

<!--/*-->
<div>
you can see me only before Thymeleaf processes me!
</div>
<!--*/-->

Isso pode ser muito útil para criar protótipos de tabelas com muitos <tr> , por exemplo:

<table>
<tr th:each="x : ${xs}">
...
</tr>
<!--/*-->
<tr>
...
</tr>
<tr>
...
</tr>
<!--*/-->
</table>

11.3 Blocos de comentário somente para protótipo do Thymeleaf

O Thymeleaf permite a de nição de blocos de comentários especiais marcados como comentários quando o modelo é aberto
estaticamente (ou seja, como um protótipo), mas considerado uma marcação normal pelo Thymeleaf ao executar o modelo.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 53/94
01/08/2020 Tutorial: Usando o Thymeleaf

<span>hello!</span>
<!--/*/
<div th:text="${...}">
...
</div>
/*/-->
<span>goodbye!</span>

O sistema de análise do Thymeleaf simplesmente removerá os marcadores <!--/*/ e /*/--> , mas não o seu conteúdo, que
cará, portanto, não comentado. Portanto, ao executar o modelo, o Thymeleaf verá o seguinte:

<span>hello!</span>

<div th:text="${...}">
...
</div>

<span>goodbye!</span>

Assim como nos blocos de comentários no nível do analisador, esse recurso é independente do dialeto.

11.4 th:blockEtiqueta sintética

O único processador de elementos do Thymeleaf (não um atributo) incluído nos Dialetos Padrão é th:block .

th:block é um mero contêiner de atributos que permite que os desenvolvedores de modelos especi quem os atributos que
desejam. O Thymeleaf executará esses atributos e simplesmente fará o bloco, mas não o conteúdo, desaparecer.

Portanto, pode ser útil, por exemplo, ao criar tabelas iteradas que requerem mais de uma <tr> para cada elemento:

<table>
<th:block th:each="user : ${users}">
<tr>
<td th:text="${user.login}">...</td>
<td th:text="${user.name}">...</td>
</tr>
<tr>
<td colspan="2" th:text="${user.address}">...</td>
</tr>
</th:block>
</table>

E especialmente útil quando usado em combinação com blocos de comentários apenas para protótipos:

<table>
<!--/*/ <th:block th:each="user : ${users}"> /*/-->
<tr>
<td th:text="${user.login}">...</td>
<td th:text="${user.name}">...</td>
</tr>
<tr>
<td colspan="2" th:text="${user.address}">...</td>
</tr>
<!--/*/ </th:block> /*/-->
</table>

Observe como essa solução permite que os modelos sejam HTML válidos (não há necessidade de adicionar <div> blocos
proibidos dentro <table> ) e ainda funciona bem quando aberto estaticamente nos navegadores como protótipos!

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 54/94
01/08/2020 Tutorial: Usando o Thymeleaf

12 Inlining

12.1 Expressão embutida

Embora o Dialeto padrão nos permita fazer quase tudo usando atributos de tag, há situações em que podemos preferir
escrever expressões diretamente em nossos textos HTML. Por exemplo, podemos preferir escrever isso:

<p>Hello, [[${session.user.name}]]!</p>

… Em vez disso:

<p>Hello, <span th:text="${session.user.name}">Sebastian</span>!</p>

Expressões entre [[...]] ou [(...)] são consideradas expressões embutidas no Thymeleaf e, dentro delas, podemos usar
qualquer tipo de expressão que também seja válida em um atributo th:text ou th:utext .

Observe que, embora [[...]] corresponda a th:text (ou seja, o resultado terá escape de HTML ), [(...)] corresponde
th:utext e não executará nenhum escape de HTML. Então, com uma variável como msg = 'This is <b>great!</b>' , dado
este fragmento:

<p>The message is "[(${msg})]"</p>

O resultado terá essas <b> tags sem escape, portanto:

<p>The message is "This is <b>great!</b>"</p>

Considerando que se escapou como:

<p>The message is "[[${msg}]]"</p>

O resultado terá escape HTML:

<p>The message is "This is &lt;b&gt;great!&lt;/b&gt;"</p>

Observe que o inlining de texto está ativo por padrão no corpo de todas as tags da nossa marcação - e não nas próprias
tags -, portanto não há nada que precisamos fazer para habilitá-lo.

Modelos inline vs naturais

Se você vem de outros mecanismos de modelo nos quais essa maneira de gerar texto é a norma, você pode estar se
perguntando: Por que não fazemos isso desde o início? É menos código que todos esses th:text atributos!

Bem, tenha cuidado lá, porque, embora você possa achar inlining bastante interessante, lembre-se sempre de que expressões
incorporadas serão exibidas literalmente em seus arquivos HTML quando você as abrir estaticamente, para que você
provavelmente não possa usá-las como protótipos de design. não mais!

A diferença entre como um navegador exibia estaticamente nosso fragmento de código sem usar inlining…

Hello, Sebastian!

... e usá-lo ...

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 55/94
01/08/2020 Tutorial: Usando o Thymeleaf

Hello, [[${session.user.name}]]!

... é bastante claro em termos de utilidade de design.

Desativando inlining

Porém, esse mecanismo pode ser desativado, porque pode haver ocasiões em que queremos gerar as seqüências [[...]] ou
[(...)] sem que seu conteúdo seja processado como uma expressão. Para isso, usaremos th:inline="none" :

<p th:inline="none">A double array looks like this: [[1, 2, 3], [4, 5]]!</p>

Isso resultará em:

<p>A double array looks like this: [[1, 2, 3], [4, 5]]!</p>

12.2 Texto embutido

O embutimento de texto é muito semelhante à capacidade de expressão embutida que acabamos de ver, mas na verdade
adiciona mais poder. Ele deve ser ativado explicitamente com th:inline="text" .

O embutimento de texto não apenas nos permite usar as mesmas expressões embutidas que acabamos de ver, mas de fato
processa os corpos das tags como se fossem modelos processados no TEXT modo de modelo, o que nos permite executar a
lógica do modelo baseado em texto (não apenas expressões de saída).

Veremos mais sobre isso no próximo capítulo sobre os modos de modelo de texto .

12.3 JavaScript embutido

O embutimento do JavaScript permite uma melhor integração dos <script> blocos JavaScript nos modelos que estão sendo
processados no HTML modo de modelo.

Como no texto embutido , isso é realmente equivalente ao processamento do conteúdo dos scripts como se fossem modelos
no JAVASCRIPT modo de modelo e, portanto, todo o poder dos modos de modelo de texto (consulte o próximo capítulo) estará
disponível. No entanto, nesta seção, focaremos em como podemos usá-lo para adicionar a saída de nossas expressões
Thymeleaf em nossos blocos JavaScript.

Este modo deve ser ativado explicitamente usando th:inline="javascript" :

<script th:inline="javascript">
...
var username = [[${session.user.name}]];
...
</script>

Isso resultará em:

<script th:inline="javascript">
...
var username = "Sebastian \"Fruity\" Applejuice";
...
</script>

Duas coisas importantes a serem observadas no código acima:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 56/94
01/08/2020 Tutorial: Usando o Thymeleaf

Primeiro , esse JavaScript embutido não apenas produzirá o texto necessário, mas também o incluirá entre aspas e o escape
do JavaScript, para que os resultados da expressão sejam exibidos como um literal JavaScript bem formado .

Em segundo lugar , que isso está acontecendo porque estamos saída da ${session.user.name} expressão como escapou , ou
seja, usando uma expressão de duplo suporte: [[${session.user.name}]] . Se, em vez disso, usamos o escape sem escape,
como:

<script th:inline="javascript">
...
var username = [(${session.user.name})];
...
</script>

O resultado seria semelhante a:

<script th:inline="javascript">
...
var username = Sebastian "Fruity" Applejuice;
...
</script>

… Que é um código JavaScript malformado. Mas produzir algo sem escape pode ser o que precisamos se estivermos
construindo partes do nosso script por meio da adição de expressões embutidas, por isso é bom ter essa ferramenta à mão.

Modelos naturais de JavaScript

A inteligência mencionada do mecanismo embutido do JavaScript vai muito além da aplicação de resultados de expressão de
escape e saída especí cos do JavaScript como literais válidos.

Por exemplo, podemos agrupar nossas expressões embutidas (escapadas) nos comentários do JavaScript, como:

<script th:inline="javascript">
...
var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit";
...
</script>

E o Thymeleaf ignorará tudo o que escrevemos após o comentário e antes do ponto e vírgula (neste caso 'Gertrud
Kiwifruit' ), portanto, o resultado da execução será parecido exatamente quando não estávamos usando os comentários
nais:

<script th:inline="javascript">
...
var username = "Sebastian \"Fruity\" Applejuice";
...
</script>

Mas dê uma outra olhada cuidadosa no código do modelo original:

<script th:inline="javascript">
...
var username = /*[[${session.user.name}]]*/ "Gertrud Kiwifruit";
...
</script>

Observe como isso é um código JavaScript válido . E ele será executado perfeitamente quando você abrir seu arquivo de
modelo de maneira estática (sem executá-lo em um servidor).

Então, o que temos aqui é uma maneira de criar modelos naturais de JavaScript !

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 57/94
01/08/2020 Tutorial: Usando o Thymeleaf

Avaliação avançada avançada e serialização de JavaScript

Uma coisa importante a ser observada em relação à inserção de JavaScript é que essa avaliação de expressão é inteligente e
não se limita a Strings. O Thymeleaf escreverá corretamente na sintaxe JavaScript os seguintes tipos de objetos:

Cordas
Números
Booleanos
Matrizes
Colecções
Mapas
Beans (objetos com métodos getter e setter )

Por exemplo, se tivéssemos o seguinte código:

<script th:inline="javascript">
...
var user = /*[[${session.user}]]*/ null;
...
</script>

Essa ${session.user} expressão será avaliada em um User objeto e o Thymeleaf a converterá corretamente na sintaxe
Javascript:

<script th:inline="javascript">
...
var user = {"age":null,"firstName":"John","lastName":"Apricot",
"name":"John Apricot","nationality":"Antarctica"};
...
</script>

A maneira como essa serialização JavaScript é feita é por meio de uma implementação da
org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer interface, que pode ser con gurada na instância da
StandardDialect utilização no mecanismo de modelo.

A implementação padrão desse mecanismo de serialização JS procurará a biblioteca Jackson no caminho de classe e, se
houver, a usará. Caso contrário, ele aplicará um mecanismo interno de serialização que cubra as necessidades da maioria dos
cenários e produz resultados semelhantes (mas é menos exível).

12.4 CSS embutido

O Thymeleaf também permite o uso de inlining em <style> tags CSS , como:

<style th:inline="css">
...
</style>

Por exemplo, digamos que temos duas variáveis de nidas para dois String valores diferentes :

classname = 'main elems'


align = 'center'

Nós poderíamos usá-los como:

<style th:inline="css">
.[[${classname}]] {
text-align: [[${align}]];
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 58/94
01/08/2020 Tutorial: Usando o Thymeleaf
}
</style>

E o resultado seria:

<style th:inline="css">
.main\ elems {
text-align: center;
}
</style>

Observe como o CSS inlining também possui alguma inteligência , assim como o JavaScript. Especi camente, expressões
emitidas por expressões escapadas como [[${classname}]] serão escapadas como identi cadores CSS . É por isso que o
nosso classname = 'main elems' se transformou main\ elems no fragmento de código acima.

Recursos avançados: modelos naturais de CSS etc.

De maneira equivalente ao explicado anteriormente para JavaScript, o CSS inlining também permite que nossas <style> tags
funcionem estaticamente e dinamicamente, ou seja, como modelos naturais de CSS por meio de agrupar expressões
embutidas nos comentários. Vejo:

<style th:inline="css">
.main\ elems {
text-align: /*[[${align}]]*/ left;
}
</style>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 59/94
01/08/2020 Tutorial: Usando o Thymeleaf

13 modos de modelo textual

13.1 Sintaxe textual

Três dos Thymeleaf modos de modelos são considerados textuais : TEXT , JAVASCRIPT e CSS . Isso os diferencia dos modos de
modelo de marcação: HTML e XML .

A principal diferença entre os modos de modelo textual e os de marcação é que, em um modelo textual, não há tags nas quais
inserir lógica na forma de atributos; portanto, precisamos con ar em outros mecanismos.

O primeiro e mais básico desses mecanismos é o inlining , que já detalhamos no capítulo anterior. A sintaxe embutida é a
maneira mais simples de gerar resultados de expressões no modo de modelo de texto, portanto, este é um modelo
perfeitamente válido para um email de texto.

Dear [(${name})],

Please find attached the results of the report you requested


with name "[(${report.name})]".

Sincerely,
The Reporter.

Mesmo sem tags, o exemplo acima é um modelo Thymeleaf completo e válido que pode ser executado no TEXT modo de
modelo.

Mas, para incluir uma lógica mais complexa do que meras expressões de saída , precisamos de uma nova sintaxe não baseada
em tag:

[# th:each="item : ${items}"]
- [(${item})]
[/]

Qual é realmente a versão condensada do mais detalhado:

[#th:block th:each="item : ${items}"]


- [#th:block th:utext="${item}" /]
[/th:block]

Observe como essa nova sintaxe é baseada em elementos (ou seja, tags processáveis) que são declarados como em
[#element ...] vez de <element ...> . Elementos estão abertos como [#element ...] e fechado como [/element] , e
marcas independentes pode ser declarado, minimizando o elemento aberto com uma / de uma maneira quase equivalente a
tags XML: [#element ... /] .

O Dialeto Padrão contém apenas um processador para um desses elementos: o já conhecido th:block , embora possamos
estender isso em nossos dialetos e criar novos elementos da maneira usual. Além disso, o th:block elemento ( [#th:block
...] ... [/th:block] ) pode ser abreviado como a string vazia ( [# ...] ... [/] ), portanto, o bloco acima é realmente
equivalente a:

[# th:each="item : ${items}"]
- [# th:utext="${item}" /]
[/]

E dado [# th:utext="${item}" /] é equivalente a uma expressão sem escape embutida , poderíamos apenas usá-la para ter
menos código. Assim, terminamos com o primeiro fragmento de código que vimos acima:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 60/94
01/08/2020 Tutorial: Usando o Thymeleaf

[# th:each="item : ${items}"]
- [(${item})]
[/]

Observe que a sintaxe textual requer equilíbrio total de elementos (sem tags não fechadas) e atributos entre aspas - é mais no
estilo XML do que no estilo HTML.

Vamos dar uma olhada em um exemplo mais completo de um TEXT modelo, um modelo de email em texto sem formatação :

Dear [(${customer.name})],

This is the list of our products:

[# th:each="prod : ${products}"]
- [(${prod.name})]. Price: [(${prod.price})] EUR/kg
[/]

Thanks,
The Thymeleaf Shop

Após a execução, o resultado disso pode ser algo como:

Dear Mary Ann Blueberry,

This is the list of our products:

- Apricots. Price: 1.12 EUR/kg


- Bananas. Price: 1.78 EUR/kg
- Apples. Price: 0.85 EUR/kg
- Watermelon. Price: 1.91 EUR/kg

Thanks,
The Thymeleaf Shop

E outro exemplo no JAVASCRIPT modo modelo, um greeter.js arquivo, processamos como modelo textual e cujo resultado
chamamos de nossas páginas HTML. Observe que este não é um <script> bloco em um modelo HTML, mas um .js arquivo
sendo processado como modelo por si só:

var greeter = function() {

var username = [[${session.user.name}]];

[# th:each="salut : ${salutations}"]
alert([[${salut}]] + " " + username);
[/]

};

Após a execução, o resultado disso pode ser algo como:

var greeter = function() {

var username = "Bertrand \"Crunchy\" Pear";

alert("Hello" + " " + username);


alert("Ol\u00E1" + " " + username);
alert("Hola" + " " + username);

};

Atributos de elemento com escape


https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 61/94
01/08/2020 Tutorial: Usando o Thymeleaf

Para evitar interações com partes do modelo que podem ser processadas em outros modos (por exemplo, text -modo
embutido dentro de um HTML modelo), o Thymeleaf 3.0 permite que os atributos nos elementos em sua sintaxe textual sejam
escapados. Assim:

Os atributos no TEXT modo de modelo terão um escape sem HTML .


Os atributos no JAVASCRIPT modo de modelo não terão escape de JavaScript .
Os atributos no CSS modo de modelo não terão CSS .

Portanto, isso seria perfeitamente bom em um TEXT modelo -mode (observe o &gt; ):

[# th:if="${120&lt;user.age}"]
Congratulations!
[/]

É claro que &lt; isso não faria sentido em um modelo de texto real , mas é uma boa idéia se estamos processando um
modelo HTML com um th:inline="text" bloco que contém o código acima e queremos garantir que nosso navegador não
aceite isso <user.age como o nome de uma tag aberta ao abrir estaticamente o arquivo como um protótipo.

13.2 Extensibilidade

Uma das vantagens dessa sintaxe é que ela é tão extensível quanto a marcação . Os desenvolvedores ainda podem de nir
seus próprios dialetos com elementos e atributos personalizados, aplicar um pre xo a eles (opcionalmente) e usá-los nos
modos de modelo de texto:

[#myorg:dosomething myorg:importantattr="211"]some text[/myorg:dosomething]

13.3 Blocos de comentários apenas para protótipos textuais: adicionando código

Os modos JAVASCRIPT e CSS modelo (não disponíveis para TEXT ) permitem incluir código entre uma sintaxe de comentário
especial, /*[+...+]*/ para que o Thymeleaf descomente automaticamente esse código ao processar o modelo:

var x = 23;

/*[+

var msg = "This is a working application";

+]*/

var f = function() {
...

Será executado como:

var x = 23;

var msg = "This is a working application";

var f = function() {
...

Você pode incluir expressões dentro desses comentários, e eles serão avaliados:

var x = 23;

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 62/94
01/08/2020 Tutorial: Usando o Thymeleaf
/*[+

var msg = "Hello, " + [[${session.user.name}]];

+]*/

var f = function() {
...

13.4 Blocos de comentários em nível de analisador de texto: removendo código

De maneira semelhante à dos blocos de comentários somente para protótipos, todos os três modos de modelo de texto (
TEXT , JAVASCRIPT e CSS ) possibilitam instruir o Thymeleaf a remover o código entre marcas /*[- */ e /* -]*/ marcas
especiais , assim:

var x = 23;

/*[- */

var msg = "This is shown only when executed statically!";

/* -]*/

var f = function() {
...

Ou isso, no TEXT modo:

...
/*[- Note the user is obtained from the session, which must exist -]*/
Welcome [(${session.user.name})]!
...

13.5 JavaScript natural e modelos CSS

Como visto no capítulo anterior, o JavaScript e o CSS inlining oferecem a possibilidade de incluir expressões incorporadas nos
comentários JavaScript / CSS, como:

...
var username = /*[[${session.user.name}]]*/ "Sebastian Lychee";
...

… Que é JavaScript válido e, uma vez executado, pode se parecer com:

...
var username = "John Apricot";
...

Esse mesmo truque de anexar expressões embutidas dentro de comentários pode de fato ser usado para toda a sintaxe do
modo de texto:

/*[# th:if="${user.admin}"]*/
alert('Welcome admin');
/*[/]*/

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 63/94
01/08/2020 Tutorial: Usando o Thymeleaf

Esse alerta no código acima será mostrado quando o modelo for aberto estaticamente - porque é 100% JavaScript válido - e
também quando o modelo for executado se o usuário for um administrador. É equivalente a:

[# th:if="${user.admin}"]
alert('Welcome admin');
[/]

… Que é realmente o código no qual a versão inicial é convertida durante a análise do modelo.

Observe, no entanto, que a disposição dos elementos nos comentários não limpa as linhas em que eles vivem (à direita até
que um ; seja encontrado), como as expressões de saída embutidas. Esse comportamento é reservado apenas para
expressões de saída embutidas.

Portanto, o Thymeleaf 3.0 permite o desenvolvimento de scripts JavaScript complexos e folhas de estilo CSS na forma de
modelos naturais , válidos como protótipo e como modelo de trabalho .

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 64/94
01/08/2020 Tutorial: Usando o Thymeleaf

14 Mais algumas páginas para nossa mercearia


Agora que sabemos muito sobre o uso do Thymeleaf, podemos adicionar novas páginas ao nosso site para gerenciamento de
pedidos.

Observe que vamos nos concentrar no código HTML, mas você pode dar uma olhada no código-fonte incluído, se desejar ver
os controladores correspondentes.

14.1 Lista de pedidos

Vamos começar criando uma página da lista de pedidos /WEB-INF/templates/order/list.html :

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>

<title>Good Thymes Virtual Grocery</title>


<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<body>

<h1>Order list</h1>

<table>
<tr>
<th>DATE</th>
<th>CUSTOMER</th>
<th>TOTAL</th>
<th></th>
</tr>
<tr th:each="o : ${orders}" th:class="${oStat.odd}? 'odd'">
<td th:text="${#calendars.format(o.date,'dd/MMM/yyyy')}">13 jan 2011</td>
<td th:text="${o.customer.name}">Frederic Tomato</td>
<td th:text="${#aggregates.sum(o.orderLines.{purchasePrice * amount})}">23.32</td>
<td>
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
</td>
</tr>
</table>

<p>
<a href="../home.html" th:href="@{/}">Return to home</a>
</p>

</body>

</html>

Não há nada aqui que deva nos surpreender, exceto por um pouco da magia do OGNL:

<td th:text="${#aggregates.sum(o.orderLines.{purchasePrice * amount})}">23.32</td>

O que isso faz é que, para cada linha de ordem ( OrderLine objeto) da ordem, multiplique suas propriedades purchasePrice e
amount (chamando o correspondente getPurchasePrice() e os getAmount() métodos) e retorne o resultado em uma lista de

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 65/94
01/08/2020 Tutorial: Usando o Thymeleaf

números, posteriormente agregados pela #aggregates.sum(...) função para obter o total da ordem preço.

Você precisa amar o poder do OGNL.

14.2 Detalhes do pedido

Agora, para a página de detalhes do pedido, na qual faremos um uso intenso da sintaxe do asterisco:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>

<body th:object="${order}">

<h1>Order details</h1>

<div>
<p><b>Code:</b> <span th:text="*{id}">99</span></p>
<p>
<b>Date:</b>
<span th:text="*{#calendars.format(date,'dd MMM yyyy')}">13 jan 2011</span>
</p>
</div>

<h2>Customer</h2>

<div th:object="*{customer}">
<p><b>Name:</b> <span th:text="*{name}">Frederic Tomato</span></p>
<p>
<b>Since:</b>
<span th:text="*{#calendars.format(customerSince,'dd MMM yyyy')}">1 jan 2011</span>
</p>
</div>

<h2>Products</h2>

<table>
<tr>
<th>PRODUCT</th>
<th>AMOUNT</th>
<th>PURCHASE PRICE</th>
</tr>
<tr th:each="ol,row : *{orderLines}" th:class="${row.odd}? 'odd'">
<td th:text="${ol.product.name}">Strawberries</td>
<td th:text="${ol.amount}" class="number">3</td>
<td th:text="${ol.purchasePrice}" class="number">23.32</td>
</tr>
</table>

<div>
<b>TOTAL:</b>
<span th:text="*{#aggregates.sum(orderLines.{purchasePrice * amount})}">35.23</span>
</div>

<p>
<a href="list.html" th:href="@{/order/list}">Return to order list</a>
</p>

</body>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 66/94
01/08/2020 Tutorial: Usando o Thymeleaf

</html>

Não há muita novidade aqui, exceto para esta seleção de objetos aninhados:

<body th:object="${order}">

...

<div th:object="*{customer}">
<p><b>Name:</b> <span th:text="*{name}">Frederic Tomato</span></p>
...
</div>

...
</body>

… O que torna isso *{name} equivalente a:

<p><b>Name:</b> <span th:text="${order.customer.name}">Frederic Tomato</span></p>

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 67/94
01/08/2020 Tutorial: Usando o Thymeleaf

15 Mais sobre con guração

15.1 Resolvedores de modelos

Para nossa Mercearia Virtual Good Thymes, escolhemos uma ITemplateResolver implementação chamada
ServletContextTemplateResolver que nos permitiu obter modelos como recursos do Contexto do Servlet.

Além de nos dar a capacidade de criar nosso próprio resolvedor de modelos implementando o
ITemplateResolver, Thymeleaf, inclui quatro implementações prontas para uso:

org.thymeleaf.templateresolver.ClassLoaderTemplateResolver , que resolve modelos como recursos do carregador de


classes, como:

return Thread.currentThread().getContextClassLoader().getResourceAsStream(template);

org.thymeleaf.templateresolver.FileTemplateResolver , que resolve modelos como arquivos do sistema de arquivos,


como:

return new FileInputStream(new File(template));

org.thymeleaf.templateresolver.UrlTemplateResolver , que resolve modelos como URLs (mesmo os não locais), como:

return (new URL(template)).openStream();

org.thymeleaf.templateresolver.StringTemplateResolver , que resolve modelos diretamente como String sendo


especi cado como template (ou nome do modelo , que neste caso é obviamente muito mais que um mero nome):

return new StringReader(templateName);

Todas as implementações pré-empacotadas ITemplateResolver permitem o mesmo conjunto de parâmetros de con guração,
que incluem:

Pre xo e su xo (como já visto):

templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");

Aliases de modelo que permitem o uso de nomes de modelo que não correspondem diretamente aos nomes de arquivo.
Se o su xo / pre xo e o alias existirem, o alias será aplicado antes do pre xo / su xo:

templateResolver.addTemplateAlias("adminHome","profiles/admin/home");
templateResolver.setTemplateAliases(aliasesMap);

Codi cação a ser aplicada ao ler modelos:

templateResolver.setCharacterEncoding("UTF-8");

Modo de modelo a ser usado:

// Default is HTML
templateResolver.setTemplateMode("XML");

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 68/94
01/08/2020 Tutorial: Usando o Thymeleaf

Modo padrão para cache de modelo e padrões para de nir se modelos especí cos podem ser armazenados em cache ou
não:

// Default is true
templateResolver.setCacheable(false);
templateResolver.getCacheablePatternSpec().addPattern("/users/*");

TTL em milissegundos para entradas de cache de modelo analisadas originadas neste resolvedor de modelos. Se não
estiver de nido, a única maneira de remover uma entrada do cache será exceder o tamanho máximo do cache (a entrada
mais antiga será removida).

// Default is no TTL (only cache size exceeded would remove entries)


templateResolver.setCacheTTLMs(60000L);

Os pacotes de integração Thymeleaf + Spring oferecem uma SpringResourceTemplateResolver implementação que


usa toda a infraestrutura Spring para acessar e ler recursos em aplicativos e qual é a implementação recomendada em
aplicativos habilitados para Spring.

Resolvedores de modelo de encadeamento

Além disso, um Mecanismo de modelos pode especi car vários resolvedores de modelos; nesse caso, um pedido pode ser
estabelecido entre eles para a resolução do modelo, de modo que, se o primeiro não puder resolver o modelo, o segundo
será solicitado e assim por diante:

ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver();


classLoaderTemplateResolver.setOrder(Integer.valueOf(1));

ServletContextTemplateResolver servletContextTemplateResolver =
new ServletContextTemplateResolver(servletContext);
servletContextTemplateResolver.setOrder(Integer.valueOf(2));

templateEngine.addTemplateResolver(classLoaderTemplateResolver);
templateEngine.addTemplateResolver(servletContextTemplateResolver);

Quando vários resolvedores de modelos são aplicados, é recomendável especi car padrões para cada resolvedor de modelos,
para que o Thymeleaf possa descartar rapidamente aqueles que não pretendem resolver o modelo, melhorando o
desempenho. Fazer isso não é um requisito, mas uma recomendação:

ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver();


classLoaderTemplateResolver.setOrder(Integer.valueOf(1));
// This classloader will not be even asked for any templates not matching these patterns
classLoaderTemplateResolver.getResolvablePatternSpec().addPattern("/layout/*.html");
classLoaderTemplateResolver.getResolvablePatternSpec().addPattern("/menu/*.html");

ServletContextTemplateResolver servletContextTemplateResolver =
new ServletContextTemplateResolver(servletContext);
servletContextTemplateResolver.setOrder(Integer.valueOf(2));

Se esses padrões resolvíveis não forem especi cados, con aremos nos recursos especí cos de cada uma das
ITemplateResolver implementações que estamos usando. Observe que nem todas as implementações podem determinar a
existência de um modelo antes de resolvê-lo e, portanto, sempre podem considerá-lo resolvível e interromper a cadeia de
resolução (não permitindo que outros resolvedores veri quem o mesmo modelo), mas não conseguem leia o recurso real.

Todas as ITemplateResolver implementações que estão incluídos com Thymeleaf núcleo incluem um mecanismo que nos
permitirá fazer os resolvedores realmente veri car se existe um recurso antes de considerá-lo resolvido . É a
checkExistence bandeira, que funciona como:

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 69/94
01/08/2020 Tutorial: Usando o Thymeleaf

ClassLoaderTemplateResolver classLoaderTemplateResolver = new ClassLoaderTemplateResolver();


classLoaderTemplateResolver.setOrder(Integer.valueOf(1));
classLoaderTempalteResolver.setCheckExistence(true);

Esse checkExistence sinalizador força o resolvedor a executar uma veri cação real da existência de recursos durante a fase
de resolução (e deixar o seguinte resolvedor da cadeia ser chamado se a veri cação da existência retornar falsa). Embora isso
possa parecer bom em todos os casos, na maioria dos casos, isso signi ca um duplo acesso ao recurso em si (uma vez para
veri car a existência, outra vez para lê-lo) e pode ser um problema de desempenho em alguns cenários, por exemplo, com
base em URL remota recursos de modelo - um possível problema de desempenho que pode ser mitigado pelo uso do cache
de modelos (nesse caso, os modelos serão resolvidos apenas na primeira vez em que forem acessados).

15.2 Resolvedores de mensagens

Não especi camos explicitamente uma implementação do Message Resolver para nosso aplicativo Grocery e, como foi
explicado anteriormente, isso signi cava que a implementação usada era um
org.thymeleaf.messageresolver.StandardMessageResolver objeto.

StandardMessageResolver é a implementação padrão da IMessageResolver interface, mas poderíamos criar nossa própria, se
quiséssemos, adaptada às necessidades especí cas de nossa aplicação.

Os pacotes de integração Thymeleaf + Spring oferecem por padrão uma IMessageResolver implementação que usa a
maneira padrão do Spring de recuperar mensagens externalizadas, usando MessageSource beans declarados no
Contexto do Aplicativo Spring.

Resolvedor de mensagens padrão

Então, como StandardMessageResolver procura as mensagens solicitadas em um modelo especí co?

Se o nome do modelo estiver home e ele estiver localizado /WEB-INF/templates/home.html , e o código do idioma solicitado
for gl_ES , esse resolvedor procurará mensagens nos seguintes arquivos, nesta ordem:

/WEB-INF/templates/home_gl_ES.properties
/WEB-INF/templates/home_gl.properties
/WEB-INF/templates/home.properties

Consulte a documentação JavaDoc da StandardMessageResolver classe para obter mais detalhes sobre como o mecanismo
completo de resolução de mensagens funciona.

Con gurando resolvedores de mensagens

E se quiséssemos adicionar um resolvedor de mensagens (ou mais) ao Mecanismo de Modelos? Fácil:

// For setting only one


templateEngine.setMessageResolver(messageResolver);

// For setting more than one


templateEngine.addMessageResolver(messageResolver);

E por que queremos ter mais de um resolvedor de mensagens? Pelo mesmo motivo que os resolvedores de modelos: os
resolvedores de mensagens são solicitados e, se o primeiro não puder resolver uma mensagem especí ca, o segundo será
solicitado, o terceiro, etc.

15.3 Serviços de conversão


https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 70/94
01/08/2020 Tutorial: Usando o Thymeleaf

O serviço de conversão que nos permite executar operações de conversão e formatação de dados por meio da sintaxe de
colchete duplo ( ${{...}} ) é realmente um recurso do Dialeto Padrão, não do próprio Mecanismo de Modelo Thymeleaf.

Como tal, a maneira de con gurá-lo é con gurando nossa implementação customizada da
IStandardConversionService interface diretamente na instância StandardDialect que está sendo con gurada no mecanismo
de modelo. Gostar:

IStandardConversionService customConversionService = ...

StandardDialect dialect = new StandardDialect();


dialect.setConversionService(customConversionService);

templateEngine.setDialect(dialect);

Observe que os pacotes thymeleaf-spring3 e thymeleaf-spring4 contêm o SpringStandardDialect , e esse dialeto já


vem pré-con gurado com uma implementação IStandardConversionService que integra a infraestrutura de serviço de
conversão da Spring no Thymeleaf.

15.4 Log

O Thymeleaf presta muita atenção ao log e sempre tenta oferecer a quantidade máxima de informações úteis por meio de
sua interface de log.

A biblioteca de log utilizada é a slf4j, que realmente atua como uma ponte para qualquer implementação de log que
possamos usar em nosso aplicativo (por exemplo log4j ).

Aulas Thymeleaf irá registrar TRACE , DEBUG e INFO informações -level, dependendo do nível de detalhe que desejamos, e
além disso o registo geral ele usará três madeireiros especiais associados com a classe TemplateEngine que podemos
con gurar separadamente para diferentes ns:

org.thymeleaf.TemplateEngine.CONFIG produzirá uma con guração detalhada da biblioteca durante a inicialização.


org.thymeleaf.TemplateEngine.TIMER produzirá informações sobre a quantidade de tempo necessária para processar
cada modelo (útil para avaliação comparativa!)
org.thymeleaf.TemplateEngine.cache é o pre xo para um conjunto de criadores de logs que produzem informações
especí cas sobre os caches. Embora os nomes dos registradores de cache sejam con guráveis pelo usuário e, portanto,
possam mudar, por padrão eles são:

org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE
org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHE

Um exemplo de con guração da infraestrutura de log da Thymeleaf, usando log4j , pode ser:

log4j.logger.org.thymeleaf=DEBUG
log4j.logger.org.thymeleaf.TemplateEngine.CONFIG=TRACE
log4j.logger.org.thymeleaf.TemplateEngine.TIMER=TRACE
log4j.logger.org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE=TRACE

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 71/94
01/08/2020 Tutorial: Usando o Thymeleaf

16 Cache de modelo
O Thymeleaf funciona graças a um conjunto de analisadores - para marcação e texto - que analisam modelos em sequências
de eventos (marca aberta, texto, marca fechada, comentário etc.) e uma série de processadores - um para cada tipo de
comportamento que precisa ser aplicado - que modi ca a sequência de eventos analisados pelo modelo para criar os
resultados que esperamos combinando o modelo original com nossos dados.

Ele também inclui - por padrão - um cache que armazena modelos analisados; a sequência de eventos resultante da leitura e
análise de arquivos de modelo antes de processá-los. Isso é especialmente útil ao trabalhar em um aplicativo Web e baseia-se
nos seguintes conceitos:

Entrada / Saída é quase sempre a parte mais lenta de qualquer aplicativo. O processamento na memória é extremamente
rápido em comparação.
A clonagem de uma sequência de eventos na memória existente é sempre muito mais rápida do que ler um arquivo de
modelo, analisá-lo e criar uma nova sequência de eventos para ele.
Os aplicativos da Web geralmente têm apenas algumas dezenas de modelos.
Os arquivos de modelo são de tamanho pequeno a médio e não são modi cados enquanto o aplicativo está em
execução.

Isso tudo leva à idéia de que o armazenamento em cache dos modelos mais usados em um aplicativo da Web é possível sem
desperdiçar grandes quantidades de memória, além de economizar muito tempo gasto em operações de entrada / saída em
um pequeno conjunto de arquivos que, de fato, nunca muda.

E como podemos controlar esse cache? Primeiro, aprendemos antes que podemos ativá-lo ou desativá-lo no Resolvedor de
modelos, mesmo atuando apenas em modelos especí cos:

// Default is true
templateResolver.setCacheable(false);
templateResolver.getCacheablePatternSpec().addPattern("/users/*");

Além disso, podemos modi car sua con guração estabelecendo nosso próprio objeto Gerenciador de Cache , que pode ser
uma instância da StandardCacheManager implementação padrão :

// Default is 200
StandardCacheManager cacheManager = new StandardCacheManager();
cacheManager.setTemplateCacheMaxSize(100);
...
templateEngine.setCacheManager(cacheManager);

Consulte a API javadoc de org.thymeleaf.cache.StandardCacheManager para obter mais informações sobre como con gurar
os caches.

As entradas podem ser removidas manualmente do cache do modelo:

// Clear the cache completely


templateEngine.clearTemplateCache();

// Clear a specific template from the cache


templateEngine.clearTemplateCacheFor("/users/userList");

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 72/94
01/08/2020 Tutorial: Usando o Thymeleaf

17 Lógica de modelos dissociada

17.1 Lógica dissociada: o conceito

Até agora, trabalhamos para nossa mercearia com modelos feitos da maneira usual , com a lógica sendo inserida em nossos
modelos na forma de atributos.

Mas o Thymeleaf também nos permite dissociar completamente a marcação do modelo de sua lógica, permitindo a criação de
modelos de marcação completamente sem lógica nos modos HTML e XML modelo.

A idéia principal é que a lógica do modelo seja de nida em um arquivo lógico separado (mais exatamente um recurso lógico ,
pois não precisa ser um arquivo ). Por padrão, esse recurso lógico será um arquivo adicional que vive no mesmo local (por
exemplo, pasta) que o arquivo de modelo, com o mesmo nome, mas com .th.xml extensão:

/templates
+->/home.html
+->/home.th.xml

Portanto, o home.html arquivo pode ser completamente sem lógica. Pode ser assim:

<!DOCTYPE html>
<html>
<body>
<table id="usersTable">
<tr>
<td class="username">Jeremy Grapefruit</td>
<td class="usertype">Normal User</td>
</tr>
<tr>
<td class="username">Alice Watermelon</td>
<td class="usertype">Administrator</td>
</tr>
</table>
</body>
</html>

Absolutamente nenhum código Thymeleaf lá. Este é um arquivo de modelo que um designer sem Thymeleaf ou
conhecimento de modelos poderia ter criado, editado e / ou entendido. Ou um fragmento de HTML fornecido por algum
sistema externo sem ganchos Thymeleaf.

Agora vamos transformar esse home.html modelo em um modelo Thymeleaf criando nosso home.th.xml arquivo adicional
como este:

<?xml version="1.0"?>
<thlogic>
<attr sel="#usersTable" th:remove="all-but-first">
<attr sel="/tr[0]" th:each="user : ${users}">
<attr sel="td.username" th:text="${user.name}" />
<attr sel="td.usertype" th:text="#{|user.type.${user.type}|}" />
</attr>
</attr>
</thlogic>

Aqui podemos ver muitas <attr> tags dentro de um thlogic bloco. Essas <attr> tags executam injeção de atributo nos nós
do modelo original selecionado por meio de seus sel atributos, que contêm seletores de marcação Thymeleaf (na verdade,
seletores de marcação AttoParser ).

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 73/94
01/08/2020 Tutorial: Usando o Thymeleaf

Observe também que as <attr> tags podem ser aninhadas para que seus seletores sejam anexados . Isso
sel="/tr[0]" acima, por exemplo, será processado como sel="#usersTable/tr[0]" . E o seletor para o nome do usuário
<td> será processado como sel="#usersTable/tr[0]//td.username" .

Portanto, uma vez mesclados, os dois arquivos vistos acima serão os mesmos:

<!DOCTYPE html>
<html>
<body>
<table id="usersTable" th:remove="all-but-first">
<tr th:each="user : ${users}">
<td class="username" th:text="${user.name}">Jeremy Grapefruit</td>
<td class="usertype" th:text="#{|user.type.${user.type}|}">Normal User</td>
</tr>
<tr>
<td class="username">Alice Watermelon</td>
<td class="usertype">Administrator</td>
</tr>
</table>
</body>
</html>

Isso parece mais familiar e é de fato menos detalhado do que a criação de dois arquivos separados. Mas a vantagem dos
modelos dissociados é que podemos dar aos nossos modelos total independência do Thymeleaf e, portanto, melhor
manutenção do ponto de vista do design.

É claro que alguns contratos entre designers ou desenvolvedores ainda serão necessários - por exemplo, o fato de os usuários
<table> precisarem de um id="usersTable" -, mas em muitos cenários um modelo HTML puro será um artefato de
comunicação muito melhor entre as equipes de design e desenvolvimento.

17.2 Con gurando modelos desacoplados

Ativando Modelos Desacoplados

A lógica desacoplada não será esperada para todos os modelos por padrão. Em vez disso, os resolvedores de modelo
con gurados (implementações de ITemplateResolver ) precisarão marcar especi camente os modelos que resolverem
usando a lógica desacoplada .

Exceto por StringTemplateResolver (que não permite lógica dissociada), todas as outras implementações
ITemplateResolver prontas para uso fornecerão um sinalizador chamado useDecoupledLogic que marcará todos os modelos
resolvidos por esse resolvedor como potencialmente possuindo toda ou parte de sua lógica vivendo em um recurso separado
:

final ServletContextTemplateResolver templateResolver =


new ServletContextTemplateResolver(servletContext);
...
templateResolver.setUseDecoupledLogic(true);

Misturando lógica acoplada e desacoplada

A lógica de modelo dissociada, quando ativada, não é um requisito. Quando ativado, signi ca que o mecanismo procurará um
recurso contendo lógica desacoplada, analisando e mesclando-o ao modelo original, se existir. Nenhum erro será gerado se o
recurso lógico dissociado não existir.

Além disso, no mesmo modelo, podemos misturar a lógica acoplada e a desacoplada , por exemplo, adicionando alguns
atributos do Thymeleaf no arquivo de modelo original, mas deixando outros para o arquivo lógico desacoplado separado. O
caso mais comum para isso é usar o novo th:ref atributo (na v3.0) .

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 74/94
01/08/2020 Tutorial: Usando o Thymeleaf

17.3 O atributo th: ref

th:ref é apenas um atributo de marcador. Ele não faz nada do ponto de vista do processamento e simplesmente desaparece
quando o modelo é processado, mas sua utilidade reside no fato de que ele atua como uma referência de marcação , ou seja,
pode ser resolvido pelo nome a partir de um seletor de marcação, como um nome de marca ou fragmento. ( th:fragment )

Portanto, se tivermos um seletor como:

<attr sel="whatever" .../>

Isso corresponderá a:

Qualquer <whatever> tag.


Quaisquer tags com um th:fragment="whatever" atributo.
Quaisquer tags com um th:ref="whatever" atributo.

Qual é a vantagem de th:ref , por exemplo, usar um id atributo HTML puro ? Apenas o fato de não querermos adicionar
tantos atributos id e class às nossas tags para atuar como âncoras lógicas , o que pode acabar poluindo nossa saída.

E no mesmo sentido, qual é a desvantagem th:ref ? Bem, obviamente, estaríamos adicionando um pouco da lógica
Thymeleaf ( "lógica" ) aos nossos modelos.

Observe que essa aplicabilidade do th:ref atributo não se aplica apenas aos arquivos de modelo lógico dissociados :
funciona da mesma maneira em outros tipos de cenários, como nas expressões de fragmento ( ~{...} ).

17.4 Impacto no desempenho de modelos dissociados

O impacto é extremamente pequeno. Quando um modelo resolvido é marcado para usar a lógica desacoplada e não é
armazenado em cache, o recurso da lógica do modelo será resolvido primeiro, analisado e processado em uma secuência de
instruções na memória: basicamente uma lista de atributos a serem injetados em cada seletor de marcação.

Mas esta é a única etapa adicional necessária porque, depois disso, o modelo real será analisado, e enquanto ele é analisado
esses atributos serão injetados on-the- y pelo próprio analisador, graças às capacidades avançadas para seleção nó na
AttoParser . Os nós analisados sairão do analisador como se tivessem seus atributos injetados gravados no arquivo de modelo
original.

A maior vantagem disso? Quando um modelo é con gurado para ser armazenado em cache, ele já será armazenado em cache,
contendo os atributos injetados. Portanto, a sobrecarga do uso de modelos desacoplados para modelos que podem ser
armazenados em cache, uma vez armazenados em cache, será absolutamente zero .

17.5 Resolução da lógica desacoplada

A maneira como o Thymeleaf resolve os recursos lógicos dissociados correspondentes a cada modelo é con gurável pelo
usuário. Ela é determinada por um ponto de extensão, o
org.thymeleaf.templateparser.markup.decoupled.IDecoupledTemplateLogicResolver , para o qual uma implementação
padrão é fornecido: StandardDecoupledTemplateLogicResolver .

O que essa implementação padrão faz?

Em primeiro lugar, aplica-se um prefix e um suffix para o nome base do recurso de modelo (obtido por meio de seu
ITemplateResource#getBaseName() método). Tanto o pre xo quanto o su xo podem ser con gurados e, por padrão, o
pre xo estará vazio e o su xo será .th.xml .
Segundo, solicita ao recurso de modelo que resolva um recurso relativo com o nome calculado por meio de seu
ITemplateResource#relative(String relativeLocation) método.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 75/94
01/08/2020 Tutorial: Usando o Thymeleaf

A implementação especí ca de IDecoupledTemplateLogicResolver a ser usada pode ser con gurada


TemplateEngine facilmente:

final StandardDecoupledTemplateLogicResolver decoupledresolver =


new StandardDecoupledTemplateLogicResolver();
decoupledResolver.setPrefix("../viewlogic/");
...
templateEngine.setDecoupledTemplateLogicResolver(decoupledResolver);

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 76/94
01/08/2020 Tutorial: Usando o Thymeleaf

18 Apêndice A: Objetos básicos de expressão


Alguns objetos e mapas variáveis estão sempre disponíveis para serem chamados. Vamos vê-los:

Objetos base

#ctx : o objeto de contexto. Uma implementação org.thymeleaf.context.IContext ou


org.thymeleaf.context.IWebContext dependendo do nosso ambiente (autônomo ou web).

Observe #vars e #root são sinônimos para o mesmo objeto, mas o uso #ctx é recomendado.

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.context.IContext
* ======================================================================
*/

${#ctx.locale}
${#ctx.variableNames}

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.context.IWebContext
* ======================================================================
*/

${#ctx.request}
${#ctx.response}
${#ctx.session}
${#ctx.servletContext}

#locale : acesso direto ao java.util.Locale associado à solicitação atual.

${#locale}

Namespaces de contexto da Web para atributos de solicitação / sessão, etc.

Ao usar o Thymeleaf em um ambiente da web, podemos usar uma série de atalhos para acessar parâmetros de solicitação,
atributos de sessão e atributos de aplicativo:

Observe que estes não são objetos de contexto , mas mapas adicionados ao contexto como variáveis; portanto, nós os
acessamos sem # . De alguma forma, eles agem como espaços para nome .

param : para recuperar parâmetros de solicitação. ${param.foo} é a String[] com os valores do foo parâmetro request,
então ${param.foo[0]} normalmente será usado para obter o primeiro valor.

/*
* ============================================================================
* See javadoc API for class org.thymeleaf.context.WebRequestParamsVariablesMap
* ============================================================================
*/

${param.foo} // Retrieves a String[] with the values of request parameter 'foo'


${param.size()}
${param.isEmpty()}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 77/94
01/08/2020 Tutorial: Usando o Thymeleaf
${param.containsKey('foo')}
...

sessão : para recuperar atributos da sessão.

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.context.WebSessionVariablesMap
* ======================================================================
*/

${session.foo} // Retrieves the session atttribute 'foo'


${session.size()}
${session.isEmpty()}
${session.containsKey('foo')}
...

application : para recuperar atributos de contexto de aplicativo / servlet.

/*
* =============================================================================
* See javadoc API for class org.thymeleaf.context.WebServletContextVariablesMap
* =============================================================================
*/

${application.foo} // Retrieves the ServletContext atttribute 'foo'


${application.size()}
${application.isEmpty()}
${application.containsKey('foo')}
...

Observe que não há necessidade de especi car um espaço para nome para acessar atributos de solicitação (ao contrário
de parâmetros de solicitação ) porque todos os atributos de solicitação são adicionados automaticamente ao contexto como
variáveis na raiz de contexto:

${myRequestAttribute}

Objetos de contexto da Web

Dentro de um ambiente da web, também há acesso direto aos seguintes objetos (observe que são objetos, não mapas /
espaços para nome):

#request : acesso direto ao javax.servlet.http.HttpServletRequest objeto associado à solicitação atual.

${#request.getAttribute('foo')}
${#request.getParameter('foo')}
${#request.getContextPath()}
${#request.getRequestName()}
...

#session : acesso direto ao javax.servlet.http.HttpSession objeto associado à solicitação atual.

${#session.getAttribute('foo')}
${#session.id}
${#session.lastAccessedTime}
...

#servletContext : acesso direto ao javax.servlet.ServletContext objeto associado à solicitação atual.

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 78/94
01/08/2020 Tutorial: Usando o Thymeleaf

${#servletContext.getAttribute('foo')}
${#servletContext.contextPath}
...

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 79/94
01/08/2020 Tutorial: Usando o Thymeleaf

19 Apêndice B: Objetos do Utilitário de Expressão

Informações de execução

#execInfo : objeto de expressão que fornece informações úteis sobre o modelo que está sendo processado nas
Expressões padrão do Thymeleaf.

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.ExecutionInfo
* ======================================================================
*/

/*
* Return the name and mode of the 'leaf' template. This means the template
* from where the events being processed were parsed. So if this piece of
* code is not in the root template "A" but on a fragment being inserted
* into "A" from another template called "B", this will return "B" as a
* name, and B's mode as template mode.
*/
${#execInfo.templateName}
${#execInfo.templateMode}

/*
* Return the name and mode of the 'root' template. This means the template
* that the template engine was originally asked to process. So if this
* piece of code is not in the root template "A" but on a fragment being
* inserted into "A" from another template called "B", this will still
* return "A" and A's template mode.
*/
${#execInfo.processedTemplateName}
${#execInfo.processedTemplateMode}

/*
* Return the stacks (actually, List<String> or List<TemplateMode>) of
* templates being processed. The first element will be the
* 'processedTemplate' (the root one), the last one will be the 'leaf'
* template, and in the middle all the fragments inserted in nested
* manner to reach the leaf from the root will appear.
*/
${#execInfo.templateNames}
${#execInfo.templateModes}

/*
* Return the stack of templates being processed similarly (and in the
* same order) to 'templateNames' and 'templateModes', but returning
* a List<TemplateData> with the full template metadata.
*/
${#execInfo.templateStack}

Mensagens

#messages : métodos utilitários para obter mensagens externalizadas dentro de expressões de variáveis, da mesma
maneira que seriam obtidas usando a #{...} sintaxe.

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Messages
* ======================================================================
*/
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 80/94
01/08/2020 Tutorial: Usando o Thymeleaf

/*
* Obtain externalized messages. Can receive a single key, a key plus arguments,
* or an array/list/set of keys (in which case it will return an array/list/set of
* externalized messages).
* If a message is not found, a default message (like '??msgKey??') is returned.
*/
${#messages.msg('msgKey')}
${#messages.msg('msgKey', param1)}
${#messages.msg('msgKey', param1, param2)}
${#messages.msg('msgKey', param1, param2, param3)}
${#messages.msgWithParams('msgKey', new Object[] {param1, param2, param3, param4})}
${#messages.arrayMsg(messageKeyArray)}
${#messages.listMsg(messageKeyList)}
${#messages.setMsg(messageKeySet)}

/*
* Obtain externalized messages or null. Null is returned instead of a default
* message if a message for the specified key is not found.
*/
${#messages.msgOrNull('msgKey')}
${#messages.msgOrNull('msgKey', param1)}
${#messages.msgOrNull('msgKey', param1, param2)}
${#messages.msgOrNull('msgKey', param1, param2, param3)}
${#messages.msgOrNullWithParams('msgKey', new Object[] {param1, param2, param3, param4})}
${#messages.arrayMsgOrNull(messageKeyArray)}
${#messages.listMsgOrNull(messageKeyList)}
${#messages.setMsgOrNull(messageKeySet)}

URIs / URLs

#uris : objeto utilitário para executar operações de URL / URL (especialmente escape / unescaping) dentro das
expressões padrão do Thymeleaf.

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Uris
* ======================================================================
*/

/*
* Escape/Unescape as a URI/URL path
*/
${#uris.escapePath(uri)}
${#uris.escapePath(uri, encoding)}
${#uris.unescapePath(uri)}
${#uris.unescapePath(uri, encoding)}

/*
* Escape/Unescape as a URI/URL path segment (between '/' symbols)
*/
${#uris.escapePathSegment(uri)}
${#uris.escapePathSegment(uri, encoding)}
${#uris.unescapePathSegment(uri)}
${#uris.unescapePathSegment(uri, encoding)}

/*
* Escape/Unescape as a Fragment Identifier (#frag)
*/
${#uris.escapeFragmentId(uri)}
${#uris.escapeFragmentId(uri, encoding)}
${#uris.unescapeFragmentId(uri)}
${#uris.unescapeFragmentId(uri, encoding)}

/*
* Escape/Unescape as a Query Parameter (?var=value)
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 81/94
01/08/2020 Tutorial: Usando o Thymeleaf
*/
${#uris.escapeQueryParam(uri)}
${#uris.escapeQueryParam(uri, encoding)}
${#uris.unescapeQueryParam(uri)}
${#uris.unescapeQueryParam(uri, encoding)}

Conversões

#conversions : objeto de utilitário que permite a execução do serviço de conversão em qualquer ponto de um modelo:

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Conversions
* ======================================================================
*/

/*
* Execute the desired conversion of the 'object' value into the
* specified class.
*/
${#conversions.convert(object, 'java.util.TimeZone')}
${#conversions.convert(object, targetClass)}

datas

#dates : métodos utilitários para java.util.Date objetos:

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Dates
* ======================================================================
*/

/*
* Format date with the standard locale format
* Also works with arrays, lists or sets
*/
${#dates.format(date)}
${#dates.arrayFormat(datesArray)}
${#dates.listFormat(datesList)}
${#dates.setFormat(datesSet)}

/*
* Format date with the ISO8601 format
* Also works with arrays, lists or sets
*/
${#dates.formatISO(date)}
${#dates.arrayFormatISO(datesArray)}
${#dates.listFormatISO(datesList)}
${#dates.setFormatISO(datesSet)}

/*
* Format date with the specified pattern
* Also works with arrays, lists or sets
*/
${#dates.format(date, 'dd/MMM/yyyy HH:mm')}
${#dates.arrayFormat(datesArray, 'dd/MMM/yyyy HH:mm')}
${#dates.listFormat(datesList, 'dd/MMM/yyyy HH:mm')}
${#dates.setFormat(datesSet, 'dd/MMM/yyyy HH:mm')}

/*
* Obtain date properties

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 82/94
01/08/2020 Tutorial: Usando o Thymeleaf
* Also works with arrays, lists or sets
*/
${#dates.day(date)} // also arrayDay(...), listDay(...), etc.
${#dates.month(date)} // also arrayMonth(...), listMonth(...), etc.
${#dates.monthName(date)} // also arrayMonthName(...), listMonthName(...), etc.
${#dates.monthNameShort(date)} // also arrayMonthNameShort(...), listMonthNameShort(...), etc.
${#dates.year(date)} // also arrayYear(...), listYear(...), etc.
${#dates.dayOfWeek(date)} // also arrayDayOfWeek(...), listDayOfWeek(...), etc.
${#dates.dayOfWeekName(date)} // also arrayDayOfWeekName(...), listDayOfWeekName(...), etc.
${#dates.dayOfWeekNameShort(date)} // also arrayDayOfWeekNameShort(...), listDayOfWeekNameShort(...), etc.
${#dates.hour(date)} // also arrayHour(...), listHour(...), etc.
${#dates.minute(date)} // also arrayMinute(...), listMinute(...), etc.
${#dates.second(date)} // also arraySecond(...), listSecond(...), etc.
${#dates.millisecond(date)} // also arrayMillisecond(...), listMillisecond(...), etc.

/*
* Create date (java.util.Date) objects from its components
*/
${#dates.create(year,month,day)}
${#dates.create(year,month,day,hour,minute)}
${#dates.create(year,month,day,hour,minute,second)}
${#dates.create(year,month,day,hour,minute,second,millisecond)}

/*
* Create a date (java.util.Date) object for the current date and time
*/
${#dates.createNow()}

${#dates.createNowForTimeZone()}

/*
* Create a date (java.util.Date) object for the current date (time set to 00:00)
*/
${#dates.createToday()}

${#dates.createTodayForTimeZone()}

Calendários

#calendars : análogo a #dates , mas para java.util.Calendar objetos:

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Calendars
* ======================================================================
*/

/*
* Format calendar with the standard locale format
* Also works with arrays, lists or sets
*/
${#calendars.format(cal)}
${#calendars.arrayFormat(calArray)}
${#calendars.listFormat(calList)}
${#calendars.setFormat(calSet)}

/*
* Format calendar with the ISO8601 format
* Also works with arrays, lists or sets
*/
${#calendars.formatISO(cal)}
${#calendars.arrayFormatISO(calArray)}
${#calendars.listFormatISO(calList)}
${#calendars.setFormatISO(calSet)}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 83/94
01/08/2020 Tutorial: Usando o Thymeleaf
/*
* Format calendar with the specified pattern
* Also works with arrays, lists or sets
*/
${#calendars.format(cal, 'dd/MMM/yyyy HH:mm')}
${#calendars.arrayFormat(calArray, 'dd/MMM/yyyy HH:mm')}
${#calendars.listFormat(calList, 'dd/MMM/yyyy HH:mm')}
${#calendars.setFormat(calSet, 'dd/MMM/yyyy HH:mm')}

/*
* Obtain calendar properties
* Also works with arrays, lists or sets
*/
${#calendars.day(date)} // also arrayDay(...), listDay(...), etc.
${#calendars.month(date)} // also arrayMonth(...), listMonth(...), etc.
${#calendars.monthName(date)} // also arrayMonthName(...), listMonthName(...), etc.
${#calendars.monthNameShort(date)} // also arrayMonthNameShort(...), listMonthNameShort(...), etc.
${#calendars.year(date)} // also arrayYear(...), listYear(...), etc.
${#calendars.dayOfWeek(date)} // also arrayDayOfWeek(...), listDayOfWeek(...), etc.
${#calendars.dayOfWeekName(date)} // also arrayDayOfWeekName(...), listDayOfWeekName(...), etc.
${#calendars.dayOfWeekNameShort(date)} // also arrayDayOfWeekNameShort(...), listDayOfWeekNameShort(...), etc.
${#calendars.hour(date)} // also arrayHour(...), listHour(...), etc.
${#calendars.minute(date)} // also arrayMinute(...), listMinute(...), etc.
${#calendars.second(date)} // also arraySecond(...), listSecond(...), etc.
${#calendars.millisecond(date)} // also arrayMillisecond(...), listMillisecond(...), etc.

/*
* Create calendar (java.util.Calendar) objects from its components
*/
${#calendars.create(year,month,day)}
${#calendars.create(year,month,day,hour,minute)}
${#calendars.create(year,month,day,hour,minute,second)}
${#calendars.create(year,month,day,hour,minute,second,millisecond)}

${#calendars.createForTimeZone(year,month,day,timeZone)}
${#calendars.createForTimeZone(year,month,day,hour,minute,timeZone)}
${#calendars.createForTimeZone(year,month,day,hour,minute,second,timeZone)}
${#calendars.createForTimeZone(year,month,day,hour,minute,second,millisecond,timeZone)}

/*
* Create a calendar (java.util.Calendar) object for the current date and time
*/
${#calendars.createNow()}

${#calendars.createNowForTimeZone()}

/*
* Create a calendar (java.util.Calendar) object for the current date (time set to 00:00)
*/
${#calendars.createToday()}

${#calendars.createTodayForTimeZone()}

Números

#numbers : métodos utilitários para objetos numéricos:

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Numbers
* ======================================================================
*/

/*
* ==========================
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 84/94
01/08/2020 Tutorial: Usando o Thymeleaf
* Formatting integer numbers
* ==========================
*/

/*
* Set minimum integer digits.
* Also works with arrays, lists or sets
*/
${#numbers.formatInteger(num,3)}
${#numbers.arrayFormatInteger(numArray,3)}
${#numbers.listFormatInteger(numList,3)}
${#numbers.setFormatInteger(numSet,3)}

/*
* Set minimum integer digits and thousands separator:
* 'POINT', 'COMMA', 'WHITESPACE', 'NONE' or 'DEFAULT' (by locale).
* Also works with arrays, lists or sets
*/
${#numbers.formatInteger(num,3,'POINT')}
${#numbers.arrayFormatInteger(numArray,3,'POINT')}
${#numbers.listFormatInteger(numList,3,'POINT')}
${#numbers.setFormatInteger(numSet,3,'POINT')}

/*
* ==========================
* Formatting decimal numbers
* ==========================
*/

/*
* Set minimum integer digits and (exact) decimal digits.
* Also works with arrays, lists or sets
*/
${#numbers.formatDecimal(num,3,2)}
${#numbers.arrayFormatDecimal(numArray,3,2)}
${#numbers.listFormatDecimal(numList,3,2)}
${#numbers.setFormatDecimal(numSet,3,2)}

/*
* Set minimum integer digits and (exact) decimal digits, and also decimal separator.
* Also works with arrays, lists or sets
*/
${#numbers.formatDecimal(num,3,2,'COMMA')}
${#numbers.arrayFormatDecimal(numArray,3,2,'COMMA')}
${#numbers.listFormatDecimal(numList,3,2,'COMMA')}
${#numbers.setFormatDecimal(numSet,3,2,'COMMA')}

/*
* Set minimum integer digits and (exact) decimal digits, and also thousands and
* decimal separator.
* Also works with arrays, lists or sets
*/
${#numbers.formatDecimal(num,3,'POINT',2,'COMMA')}
${#numbers.arrayFormatDecimal(numArray,3,'POINT',2,'COMMA')}
${#numbers.listFormatDecimal(numList,3,'POINT',2,'COMMA')}
${#numbers.setFormatDecimal(numSet,3,'POINT',2,'COMMA')}

/*
* =====================
* Formatting currencies
* =====================
*/

${#numbers.formatCurrency(num)}
${#numbers.arrayFormatCurrency(numArray)}
${#numbers.listFormatCurrency(numList)}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 85/94
01/08/2020 Tutorial: Usando o Thymeleaf
${#numbers.setFormatCurrency(numSet)}

/*
* ======================
* Formatting percentages
* ======================
*/

${#numbers.formatPercent(num)}
${#numbers.arrayFormatPercent(numArray)}
${#numbers.listFormatPercent(numList)}
${#numbers.setFormatPercent(numSet)}

/*
* Set minimum integer digits and (exact) decimal digits.
*/
${#numbers.formatPercent(num, 3, 2)}
${#numbers.arrayFormatPercent(numArray, 3, 2)}
${#numbers.listFormatPercent(numList, 3, 2)}
${#numbers.setFormatPercent(numSet, 3, 2)}

/*
* ===============
* Utility methods
* ===============
*/

/*
* Create a sequence (array) of integer numbers going
* from x to y
*/
${#numbers.sequence(from,to)}
${#numbers.sequence(from,to,step)}

Cordas

#strings : métodos utilitários para String objetos:

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Strings
* ======================================================================
*/

/*
* Null-safe toString()
*/
${#strings.toString(obj)} // also array*, list* and set*

/*
* Check whether a String is empty (or null). Performs a trim() operation before check
* Also works with arrays, lists or sets
*/
${#strings.isEmpty(name)}
${#strings.arrayIsEmpty(nameArr)}
${#strings.listIsEmpty(nameList)}
${#strings.setIsEmpty(nameSet)}

/*
* Perform an 'isEmpty()' check on a string and return it if false, defaulting to
* another specified string if true.
* Also works with arrays, lists or sets
*/
${#strings.defaultString(text,default)}
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 86/94
01/08/2020 Tutorial: Usando o Thymeleaf
${#strings.arrayDefaultString(textArr,default)}
${#strings.listDefaultString(textList,default)}
${#strings.setDefaultString(textSet,default)}

/*
* Check whether a fragment is contained in a String
* Also works with arrays, lists or sets
*/
${#strings.contains(name,'ez')} // also array*, list* and set*
${#strings.containsIgnoreCase(name,'ez')} // also array*, list* and set*

/*
* Check whether a String starts or ends with a fragment
* Also works with arrays, lists or sets
*/
${#strings.startsWith(name,'Don')} // also array*, list* and set*
${#strings.endsWith(name,endingFragment)} // also array*, list* and set*

/*
* Substring-related operations
* Also works with arrays, lists or sets
*/
${#strings.indexOf(name,frag)} // also array*, list* and set*
${#strings.substring(name,3,5)} // also array*, list* and set*
${#strings.substringAfter(name,prefix)} // also array*, list* and set*
${#strings.substringBefore(name,suffix)} // also array*, list* and set*
${#strings.replace(name,'las','ler')} // also array*, list* and set*

/*
* Append and prepend
* Also works with arrays, lists or sets
*/
${#strings.prepend(str,prefix)} // also array*, list* and set*
${#strings.append(str,suffix)} // also array*, list* and set*

/*
* Change case
* Also works with arrays, lists or sets
*/
${#strings.toUpperCase(name)} // also array*, list* and set*
${#strings.toLowerCase(name)} // also array*, list* and set*

/*
* Split and join
*/
${#strings.arrayJoin(namesArray,',')}
${#strings.listJoin(namesList,',')}
${#strings.setJoin(namesSet,',')}
${#strings.arraySplit(namesStr,',')} // returns String[]
${#strings.listSplit(namesStr,',')} // returns List<String>
${#strings.setSplit(namesStr,',')} // returns Set<String>

/*
* Trim
* Also works with arrays, lists or sets
*/
${#strings.trim(str)} // also array*, list* and set*

/*
* Compute length
* Also works with arrays, lists or sets
*/
${#strings.length(str)} // also array*, list* and set*

/*
* Abbreviate text making it have a maximum size of n. If text is bigger, it
* will be clipped and finished in "..."
* Also works with arrays, lists or sets
*/

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 87/94
01/08/2020 Tutorial: Usando o Thymeleaf
${#strings.abbreviate(str,10)} // also array*, list* and set*

/*
* Convert the first character to upper-case (and vice-versa)
*/
${#strings.capitalize(str)} // also array*, list* and set*
${#strings.unCapitalize(str)} // also array*, list* and set*

/*
* Convert the first character of every word to upper-case
*/
${#strings.capitalizeWords(str)} // also array*, list* and set*
${#strings.capitalizeWords(str,delimiters)} // also array*, list* and set*

/*
* Escape the string
*/
${#strings.escapeXml(str)} // also array*, list* and set*
${#strings.escapeJava(str)} // also array*, list* and set*
${#strings.escapeJavaScript(str)} // also array*, list* and set*
${#strings.unescapeJava(str)} // also array*, list* and set*
${#strings.unescapeJavaScript(str)} // also array*, list* and set*

/*
* Null-safe comparison and concatenation
*/
${#strings.equals(first, second)}
${#strings.equalsIgnoreCase(first, second)}
${#strings.concat(values...)}
${#strings.concatReplaceNulls(nullValue, values...)}

/*
* Random
*/
${#strings.randomAlphanumeric(count)}

Objetos

#objects : métodos utilitários para objetos em geral

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Objects
* ======================================================================
*/

/*
* Return obj if it is not null, and default otherwise
* Also works with arrays, lists or sets
*/
${#objects.nullSafe(obj,default)}
${#objects.arrayNullSafe(objArray,default)}
${#objects.listNullSafe(objList,default)}
${#objects.setNullSafe(objSet,default)}

Booleanos

#bools : métodos utilitários para avaliação booleana

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Bools

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 88/94
01/08/2020 Tutorial: Usando o Thymeleaf
* ======================================================================
*/

/*
* Evaluate a condition in the same way that it would be evaluated in a th:if tag
* (see conditional evaluation chapter afterwards).
* Also works with arrays, lists or sets
*/
${#bools.isTrue(obj)}
${#bools.arrayIsTrue(objArray)}
${#bools.listIsTrue(objList)}
${#bools.setIsTrue(objSet)}

/*
* Evaluate with negation
* Also works with arrays, lists or sets
*/
${#bools.isFalse(cond)}
${#bools.arrayIsFalse(condArray)}
${#bools.listIsFalse(condList)}
${#bools.setIsFalse(condSet)}

/*
* Evaluate and apply AND operator
* Receive an array, a list or a set as parameter
*/
${#bools.arrayAnd(condArray)}
${#bools.listAnd(condList)}
${#bools.setAnd(condSet)}

/*
* Evaluate and apply OR operator
* Receive an array, a list or a set as parameter
*/
${#bools.arrayOr(condArray)}
${#bools.listOr(condList)}
${#bools.setOr(condSet)}

Matrizes

#arrays : métodos utilitários para matrizes

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Arrays
* ======================================================================
*/

/*
* Converts to array, trying to infer array component class.
* Note that if resulting array is empty, or if the elements
* of the target object are not all of the same class,
* this method will return Object[].
*/
${#arrays.toArray(object)}

/*
* Convert to arrays of the specified component class.
*/
${#arrays.toStringArray(object)}
${#arrays.toIntegerArray(object)}
${#arrays.toLongArray(object)}
${#arrays.toDoubleArray(object)}
${#arrays.toFloatArray(object)}
${#arrays.toBooleanArray(object)}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 89/94
01/08/2020 Tutorial: Usando o Thymeleaf
/*
* Compute length
*/
${#arrays.length(array)}

/*
* Check whether array is empty
*/
${#arrays.isEmpty(array)}

/*
* Check if element or elements are contained in array
*/
${#arrays.contains(array, element)}
${#arrays.containsAll(array, elements)}

Listas

#lists : métodos utilitários para listas

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Lists
* ======================================================================
*/

/*
* Converts to list
*/
${#lists.toList(object)}

/*
* Compute size
*/
${#lists.size(list)}

/*
* Check whether list is empty
*/
${#lists.isEmpty(list)}

/*
* Check if element or elements are contained in list
*/
${#lists.contains(list, element)}
${#lists.containsAll(list, elements)}

/*
* Sort a copy of the given list. The members of the list must implement
* comparable or you must define a comparator.
*/
${#lists.sort(list)}
${#lists.sort(list, comparator)}

Conjuntos

#sets : métodos utilitários para conjuntos

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Sets
* ======================================================================

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 90/94
01/08/2020 Tutorial: Usando o Thymeleaf
*/

/*
* Converts to set
*/
${#sets.toSet(object)}

/*
* Compute size
*/
${#sets.size(set)}

/*
* Check whether set is empty
*/
${#sets.isEmpty(set)}

/*
* Check if element or elements are contained in set
*/
${#sets.contains(set, element)}
${#sets.containsAll(set, elements)}

Mapas

#maps : métodos utilitários para mapas

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Maps
* ======================================================================
*/

/*
* Compute size
*/
${#maps.size(map)}

/*
* Check whether map is empty
*/
${#maps.isEmpty(map)}

/*
* Check if key/s or value/s are contained in maps
*/
${#maps.containsKey(map, key)}
${#maps.containsAllKeys(map, keys)}
${#maps.containsValue(map, value)}
${#maps.containsAllValues(map, value)}

Agregados

#aggregates : métodos utilitários para criar agregações em matrizes ou coleções

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Aggregates
* ======================================================================
*/

/*

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 91/94
01/08/2020 Tutorial: Usando o Thymeleaf
* Compute sum. Returns null if array or collection is empty
*/
${#aggregates.sum(array)}
${#aggregates.sum(collection)}

/*
* Compute average. Returns null if array or collection is empty
*/
${#aggregates.avg(array)}
${#aggregates.avg(collection)}

IDs

#ids : métodos utilitários para lidar com id atributos que podem ser repetidos (por exemplo, como resultado de uma
iteração).

/*
* ======================================================================
* See javadoc API for class org.thymeleaf.expression.Ids
* ======================================================================
*/

/*
* Normally used in th:id attributes, for appending a counter to the id attribute value
* so that it remains unique even when involved in an iteration process.
*/
${#ids.seq('someId')}

/*
* Normally used in th:for attributes in <label> tags, so that these labels can refer to Ids
* generated by means if the #ids.seq(...) function.
*
* Depending on whether the <label> goes before or after the element with the #ids.seq(...)
* function, the "next" (label goes before "seq") or the "prev" function (label goes after
* "seq") function should be called.
*/
${#ids.next('someId')}
${#ids.prev('someId')}

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 92/94
01/08/2020 Tutorial: Usando o Thymeleaf

20 Apêndice C: Sintaxe do seletor de marcação


Os seletores de marcação do Thymeleaf são emprestados diretamente da biblioteca de análise do Thymeleaf : AttoParser .

A sintaxe para esses seletores tem grandes semelhanças com a dos seletores no XPath, CSS e jQuery, o que os torna fáceis de
usar para a maioria dos usuários. Você pode dar uma olhada na referência completa da sintaxe na documentação do
AttoParser .

Por exemplo, o seletor a seguir selecionará todos <div> os membros da classe content , em todas as posições dentro da
marcação (observe que isso não é tão conciso quanto possível, leia para saber o porquê):

<div th:insert="mytemplate :: //div[@class='content']">...</div>

A sintaxe básica inclui:

/x signi ca lhos diretos do nó atual com o nome x.

//x signi ca lhos do nó atual com o nome x, em qualquer profundidade.

x[@z="v"] signi ca elementos com o nome x e um atributo chamado z com o valor "v".

x[@z1="v1" and @z2="v2"] signi ca elementos com o nome x e os atributos z1 e z2 com os valores "v1" e "v2",
respectivamente.

x[i] signi ca elemento com o nome x posicionado no número i entre seus irmãos.

x[@z="v"][i] signi ca elementos com o nome x, atributo z com o valor "v" e posicionado no número i entre seus irmãos
que também correspondem a essa condição.

Mas uma sintaxe mais concisa também pode ser usada:

x é exatamente equivalente a //x (pesquise um elemento com nome ou referência x em qualquer nível de
profundidade, uma referência sendo um th:ref ou um th:fragment atributo).

Os seletores também são permitidos sem o nome / referência do elemento, desde que incluam uma especi cação de
argumentos. Assim, [@class='oneclass'] é um seletor válido que procura por quaisquer elementos (tags) com um
atributo de classe com valor "oneclass" .

Recursos avançados de seleção de atributos:

Além de = (igual), outros operadores de comparação também são válidos: != (não é igual), ^= (começa com) e $=
(termina com). Por exemplo: x[@class^='section'] signi ca elementos com nome x e um valor para o atributo
class que começa com section .

Os atributos podem ser especi cados, começando com @ (estilo XPath) e sem (estilo jQuery). Então x[z='v'] é
equivalente a x[@z='v'] .

Modi cadores de atributos múltiplos podem ser unidos ao and (estilo XPath) e também encadeando vários
modi cadores (estilo jQuery). Então, x[@z1='v1' and @z2='v2'] é realmente equivalente a x[@z1='v1'][@z2='v2'] (e
também a x[z1='v1'][z2='v2'] ).

Seletores diretos do tipo jQuery :

x.oneclass é equivalente a x[class='oneclass'] .

.oneclass é equivalente a [class='oneclass'] .

x#oneid é equivalente a x[id='oneid'] .

#oneid é equivalente a [id='oneid'] .

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 93/94
01/08/2020 Tutorial: Usando o Thymeleaf

x%oneref signi ca <x> tags que possuem um th:ref="oneref" ou th:fragment="oneref" atributo.

%oneref signi ca qualquer tag que tenha um th:ref="oneref" ou th:fragment="oneref" atributo. Observe que isso é
equivalente a simplesmente oneref porque as referências podem ser usadas em vez dos nomes dos elementos.

Seletores diretos e seletores de atributo podem ser misturados: a.external[@href^='https'] .

Portanto, a expressão do Seletor de marcação acima:

<div th:insert="mytemplate :: //div[@class='content']">...</div>

Pode ser escrito como:

<div th:insert="mytemplate :: div.content">...</div>

Examinando um exemplo diferente, este:

<div th:replace="mytemplate :: myfrag">...</div>

Irá procurar uma th:fragment="myfrag" assinatura de fragmento (ou th:ref referências). Mas também procuraria por tags
com nome myfrag se elas existissem (o que não existe, em HTML). Observe a diferença com:

<div th:replace="mytemplate :: .myfrag">...</div>

… Que procurará elementos com class="myfrag" , sem se importar com th:fragment assinaturas (ou th:ref referências).

Correspondência de classe com valores múltiplos

Marcação Seletores entender o atributo de classe a ser valores múltiplos , e, portanto, permitir a aplicação de seletores
nesse atributo mesmo se o elemento tem vários valores de classe.

Por exemplo, div.two corresponderá <div class="one two three" />

https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 94/94

Você também pode gostar