Escolar Documentos
Profissional Documentos
Cultura Documentos
Thymeleaf
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 1/94
01/08/2020 Tutorial: Usando o Thymeleaf
1 Introdução ao 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.
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
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.
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:
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
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 :
Nosso aplicativo também terá uma camada de serviço muito simples, composta por Service objetos contendo métodos
como:
...
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 4/94
01/08/2020 Tutorial: Usando o Thymeleaf
return ProductRepository.getInstance().findAll();
}
Na camada web, nosso aplicativo terá um ltro que delegará a execução aos comandos habilitados para Thymeleaf,
dependendo da URL da solicitação:
try {
/*
* 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
Tudo o que precisamos fazer agora é criar implementações da IGTVGController interface, recuperando dados dos serviços e
processando modelos usando o ITemplateEngine objeto.
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).
...
private final TemplateEngine templateEngine;
...
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));
...
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
ServletContextTemplateResolver templateResolver =
new ServletContextTemplateResolver(servletContext);
Os resolvedores de modelos são objetos que implementam uma interface da API Thymeleaf chamada
org.thymeleaf.templateresolver.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.
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.
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
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
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>
</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>
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.
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:
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.
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 .
É 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:
WebContext ctx =
new WebContext(request, response, servletContext, request.getLocale());
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.
Existe uma extensão especializada dessa interface, org.thymeleaf.context.IWebContext destinada a ser usada em aplicativos
Web baseados em ServletAPI (como o SpringMVC).
A biblioteca principal do Thymeleaf oferece uma implementação de cada uma destas interfaces:
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 .
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 .
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:
<!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>
</body>
</html>
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?
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"):
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 12/94
01/08/2020 Tutorial: Usando o Thymeleaf
Agora vamos adicionar mais conteúdo à nossa página inicial. Por exemplo, podemos exibir a data abaixo da nossa mensagem
de boas-vindas, assim:
Primeiro, teremos que modi car nosso controlador para adicionar essa data como uma variável de contexto:
WebContext ctx =
new WebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));
Adicionamos uma String variável chamada today ao nosso contexto e agora podemos exibi-la em nosso modelo:
<body>
</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
Já vimos dois tipos de valores de atributos válidos expressos nesta sintaxe: mensagem e expressões variáveis:
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:
Literais
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:
Comparações e igualdade:
Operadores condicionais:
Fichas especiais:
Nenhuma operação: _
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 14/94
01/08/2020 Tutorial: Usando o Thymeleaf
4.1 Mensagens
…para isso:
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?
Isso signi ca que precisaríamos adicionar um parâmetro à nossa mensagem. Bem assim:
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.
<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).
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>
((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('-')}
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:
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.
Você pode veri car quais as funções são oferecidos por cada um desses objetos utilitários no Apêndice B .
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 :
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());
<p>
Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011</span>
</p>
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>
<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>
<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>
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: @{...}
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.
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>
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?
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):
Mas eles podem ser usados em qualquer lugar, assim como qualquer outra variável:
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 booleanos
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:
O literal nulo
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>
As substituições literais permitem uma formatação fácil de strings contendo valores de variáveis sem a necessidade de
anexar literais '...' + '...' .
O que equivale a:
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.
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):
Observe que existem aliases textuais para alguns desses operadores: div ( / ), mod ( % ).
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 < e > .
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 ( != ).
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 ):
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 ( '...' ).
Outras expressões também podem ser omitidas; nesse caso, um valor nulo será retornado se a condição for falsa:
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.
<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:
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>
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:
… 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:
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.
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:
Observe que a etapa de pré-processamento para um código de idioma francês criará o seguinte equivalente:
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 25/94
01/08/2020 Tutorial: Usando o Thymeleaf
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:
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}" />
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 26/94
01/08/2020 Tutorial: Usando o Thymeleaf
… É 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 ).
Isso parece muito melhor! Vamos tentar fazer o mesmo com o action atributo na form tag:
E você se lembra daqueles th:href que colocamos em nosso home.html antes? Eles são exatamente esse mesmo tipo de
atributos:
Existem muitos atributos como estes, cada um deles direcionado a um atributo HTML5 especí co:
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 27/94
01/08/2020 Tutorial: Usando o Thymeleaf
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
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
<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}" />
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:
Se você processar este modelo com a cssStyle variável de nida como "warning" , obterá:
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:
(Não se preocupe com esse th:each atributo. É um atributo de iteração e falaremos sobre isso mais tarde.)
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.
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
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.
<span th:whatever="${user.name}">...</span>
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.
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.
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:
<!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:
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:
Ao usar th:each , o Thymeleaf oferece um mecanismo útil para acompanhar o status da sua iteração: a variável status .
<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.
<!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>
À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
À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.
<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.
<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>
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:
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>
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 .
<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
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">
© 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>
...
</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>
...
</body>
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
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.
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:
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.
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">
© 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>
...
</body>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 39/94
01/08/2020 Tutorial: Usando o Thymeleaf
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.
<footer th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</footer>
<body>
...
</body>
<body>
...
<div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
</div>
<footer>
© 2011 The Good Thymes Virtual Grocery
</footer>
<div>
© 2011 The Good Thymes Virtual Grocery
</div>
</body>
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:
Isso requer o uso de uma dessas duas sintaxes para chamar o fragmento de th:insert ou th:replace :
<div th:fragment="frag">
...
</div>
Poderíamos usar a segunda sintaxe especi cada acima para chamá-los (e somente a segunda):
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.
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.
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 .
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)">
</head>
...
<head th:replace="base :: common_header(~{::title},~{::link})">
<title>Awesome - Main</title>
</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>
</head>
...
Uma expressão de fragmento especial, o fragmento vazio ( ~{} ), pode ser usada para especi car nenhuma marcação . Usando
o exemplo anterior:
<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>
</head>
...
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>
</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 ):
Então o resultado é:
...
<head>
</head>
...
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>
...
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 .
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>
<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:
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 ).
Observe também que th:remove considera null um sinônimo para none , portanto, o seguinte funciona da mesma forma que
o exemplo acima:
Nesse caso, se ${condition} for falso, null será retornado e, portanto, nenhuma remoção será executada.
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:
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>
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
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 é:
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
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:
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:
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>
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.
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
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:
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:
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.
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!
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 55/94
01/08/2020 Tutorial: Usando o Thymeleaf
Hello, [[${session.user.name}]]!
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>
<p>A double array looks like this: [[1, 2, 3], [4, 5]]!</p>
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 .
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.
<script th:inline="javascript">
...
var username = [[${session.user.name}]];
...
</script>
<script th:inline="javascript">
...
var username = "Sebastian \"Fruity\" Applejuice";
...
</script>
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>
<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.
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>
<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
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 )
<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).
<style th:inline="css">
...
</style>
Por exemplo, digamos que temos duas variáveis de nidas para dois String valores diferentes :
<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.
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
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})],
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})]
[/]
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})],
[# th:each="prod : ${products}"]
- [(${prod.name})]. Price: [(${prod.price})] EUR/kg
[/]
Thanks,
The Thymeleaf Shop
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ó:
[# th:each="salut : ${salutations}"]
alert([[${salut}]] + " " + username);
[/]
};
};
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:
Portanto, isso seria perfeitamente bom em um TEXT modelo -mode (observe o > ):
[# th:if="${120<user.age}"]
Congratulations!
[/]
É claro que < 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:
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 f = function() {
...
var x = 23;
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 f = function() {
...
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 f = function() {
...
...
/*[- Note the user is obtained from the session, which must exist -]*/
Welcome [(${session.user.name})]!
...
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";
...
...
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
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.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<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:
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.
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>
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 67/94
01/08/2020 Tutorial: Usando o Thymeleaf
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:
return Thread.currentThread().getContextClassLoader().getResourceAsStream(template);
org.thymeleaf.templateresolver.UrlTemplateResolver , que resolve modelos como URLs (mesmo os não locais), como:
Todas as implementações pré-empacotadas ITemplateResolver permitem o mesmo conjunto de parâmetros de con guração,
que incluem:
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);
templateResolver.setCharacterEncoding("UTF-8");
// 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).
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:
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:
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
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).
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.
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.
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.
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:
templateEngine.setDialect(dialect);
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.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.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 72/94
01/08/2020 Tutorial: Usando o Thymeleaf
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.
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
:
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
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 )
Isso corresponderá a:
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 ( ~{...} ).
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 .
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 .
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
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 76/94
01/08/2020 Tutorial: Usando o Thymeleaf
Objetos base
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}
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
* ============================================================================
*/
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 77/94
01/08/2020 Tutorial: Usando o Thymeleaf
${param.containsKey('foo')}
...
/*
* ======================================================================
* See javadoc API for class org.thymeleaf.context.WebSessionVariablesMap
* ======================================================================
*/
/*
* =============================================================================
* See javadoc API for class org.thymeleaf.context.WebServletContextVariablesMap
* =============================================================================
*/
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}
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.getAttribute('foo')}
${#request.getParameter('foo')}
${#request.getContextPath()}
${#request.getRequestName()}
...
${#session.getAttribute('foo')}
${#session.id}
${#session.lastAccessedTime}
...
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
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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
/*
* ======================================================================
* 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
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ê):
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.
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" .
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'] ).
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 93/94
01/08/2020 Tutorial: Usando o Thymeleaf
%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.
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:
… Que procurará elementos com class="myfrag" , sem se importar com th:fragment assinaturas (ou th:ref referências).
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.
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html 94/94