Primeira parteSegunda parte7 Mensagens de verificação e erro
A maioria dos nossos formulários deve exibir mensagens de validação para informar o usuário sobre os erros que ele cometeu.
O Thymeleaf oferece várias ferramentas para isso: várias funções no objeto
#fields ,
atributos th: errors e
th: errorclass .
7.1 Erros de campo
Vamos ver como podemos definir uma classe CSS específica para um campo se ele contiver um erro:
<input type="text" th:field="*{datePlanted}" th:class="${#fields.hasErrors('datePlanted')}? fieldError" />
Como você pode ver, a função
# fields.hasErrors (...) recebe a expressão do campo como um parâmetro (
datePlanted ) e retorna um valor booleano indicando se há algum erro de validação nesse campo.
Também podemos obter todos os erros desse campo e repeti-los:
<ul> <li th:each="err : ${#fields.errors('datePlanted')}" th:text="${err}" /> </ul>
Em vez de iterar, também poderíamos usar
th: errors , um atributo especializado que cria uma lista com todos os erros do seletor especificado, separados por <br />:
<input type="text" th:field="*{datePlanted}" /> <p th:if="${#fields.hasErrors('datePlanted')}" th:errors="*{datePlanted}">Incorrect date</p>
Estilo CSS com base em erro:
th: errorclassO exemplo que vimos acima, definindo a classe CSS para o formulário de entrada, se houver erros nesse campo, é tão comum que o Thymeleaf oferece um atributo especial para uma execução precisa:
th: errorclass .
Aplicado à marca do campo de formulário (entrada, seleção, área de texto ...), ela lerá o nome do campo a ser verificado a partir de qualquer
nome existente ou
dos atributos
th: field na mesma marca e, em seguida, incluirá a classe CSS especificada na marca, se esse campo tem algum erro relacionado:
<input type="text" th:field="*{datePlanted}" class="small" th:errorclass="fieldError" />
Se houver erros no
datePlanted , será semelhante a este:
<input type="text" id="datePlanted" name="datePlanted" value="2013-01-01" class="small fieldError" />
7.2 Todos os erros
Mas e se quisermos mostrar todos os erros no formulário? Só precisamos solicitar os
métodos # fields.hasErrors (...) e
# fields.errors (...) com as constantes '
* ' ou '
all ' (que são equivalentes):
<ul th:if="${#fields.hasErrors('*')}"> <li th:each="err : ${#fields.errors('*')}" th:text="${err}">Input is incorrect</li> </ul>
Como nos exemplos acima, podemos obter todos os erros e iterá-los ...
<ul> <li th:each="err : ${#fields.errors('*')}" th:text="${err}" /> </ul>
... e também crie uma lista compartilhada <br />:
<p th:if="${#fields.hasErrors('all')}" th:errors="*{all}">Incorrect date</p>
Por fim, observe que
# fields.hasErrors ('*') é equivalente a
# fields.hasAnyErrors () e
# fields.errors ('*') é equivalente a
# fields.allErrors () . Use a sintaxe que você preferir:
<div th:if="${#fields.hasAnyErrors()}"> <p th:each="err : ${#fields.allErrors()}" th:text="${err}">...</p> </div>
7.3 Erros globais
Há um terceiro tipo de erro no formulário Spring: erros globais. Esses são erros que não estão associados a nenhum campo específico no formulário, mas ainda existem.
O Thymeleaf oferece uma constante
global para acessar estes erros:
<ul th:if="${#fields.hasErrors('global')}"> <li th:each="err : ${#fields.errors('global')}" th:text="${err}">Input is incorrect</li> </ul>
<p th:if="${#fields.hasErrors('global')}" th:errors="*{global}">Incorrect date</p>
... a, bem como os métodos auxiliares equivalentes
# fields.hasGlobalErrors () e
# fields.globalErrors () :
7.4 Exibindo erros fora dos formulários
Os erros de validação de formulário também podem ser exibidos fora dos formulários usando variáveis (
$ {...} ) em vez de expressões selecionadas (
* {...} ) e um prefixo para o nome do componente que suporta o formulário:
<div th:errors="${myForm}">...</div> <div th:errors="${myForm.date}">...</div> <div th:errors="${myForm.*}">...</div> <div th:if="${#fields.hasErrors('${myForm}')}">...</div> <div th:if="${#fields.hasErrors('${myForm.date}')}">...</div> <div th:if="${#fields.hasErrors('${myForm.*}')}">...</div> <form th:object="${myForm}"> ... </form>
7.5 Objetos de erro avançados
O Thymeleaf oferece a possibilidade de obter informações sobre erros de formulário na forma de componentes de bean (em vez de cadeias simples) com os atributos
fieldName (String),
mensagem (String) e
global (boolean).
Esses erros podem ser obtidos usando o método do utilitário
# fields.detailedErrors () :
<ul> <li th:each="e : ${#fields.detailedErrors()}" th:class="${e.global}? globalerr : fielderr"> <span th:text="${e.global}? '*' : ${e.fieldName}">The field name</span> | <span th:text="${e.message}">The error message</span> </li> </ul>
8 Este ainda é um protótipo!
Nossa aplicação está pronta. Mas vamos dar uma outra olhada na página .html que criamos ...
Uma das consequências mais agradáveis de trabalhar com o Thymeleaf é que, depois de todas essas funções que adicionamos ao nosso HTML, ainda podemos usá-lo como um protótipo (dizemos que esse é um
modelo natural ). Vamos abrir o
seedstartermng.html diretamente em nosso navegador sem iniciar nosso aplicativo:

Aqui está! Este não é um aplicativo de trabalho, não são dados reais ... mas é um protótipo completamente correto, composto de código HTML perfeitamente exibido.
9 O serviço de conversão
9.1 Configuração
Conforme explicado anteriormente, a Thymeleaf pode usar o Serviço de Transformação registrado no contexto do aplicativo. Nossa classe de configuração de aplicativos, expandindo o
auxiliar Spring
WebMvcConfigurerAdapter nativo, registrará automaticamente um serviço de conversão que podemos configurar, adicionando as ferramentas de formatação necessárias. Vamos ver como fica novamente:
@Override public void addFormatters(final FormatterRegistry registry) { super.addFormatters(registry); registry.addFormatter(varietyFormatter()); registry.addFormatter(dateFormatter()); } @Bean public VarietyFormatter varietyFormatter() { return new VarietyFormatter(); } @Bean public DateFormatter dateFormatter() { return new DateFormatter(); }
9.2 Sintaxe de colchetes duplos
O serviço de conversão pode ser facilmente aplicado para converter / formatar qualquer objeto em uma string. Isso é feito usando a sintaxe da expressão de parênteses duplos:
- Para expressões variáveis: $ {{...}}
- Para expressar uma escolha: * {{...}}
Portanto, por exemplo, dado o conversor de número inteiro em cadeia, que adiciona vírgulas como separador de milhares, é o seguinte:
<p th:text="${val}">...</p> <p th:text="${{val}}">...</p>
... deve resultar em:
<p>1234567890</p> <p>1,234,567,890</p>
9.3 Uso em formulários
Vimos anteriormente que todo atributo
th: field sempre aplicará um serviço de transformação, portanto, este:
<input type="text" th:field="*{datePlanted}" />
... na verdade equivalente a:
<input type="text" th:field="*{{datePlanted}}" />
Observe que, conforme o requisito do Spring, este é o único cenário em que o serviço de transformação é aplicado a expressões usando a sintaxe de colchete único.
9.4 #conversions objeto de conversão
O objeto utilitário de conversão #conversions permite iniciar manualmente o serviço de conversão, quando necessário:
<p th:text="${'Val: ' + #conversions.convert(val,'String')}">...</p>
A sintaxe para este objeto de serviço é:
- # conversions.convert (Object, Class) : converte o objeto na classe especificada
- # conversions.convert (Object, String) : o mesmo que acima, mas com a classe de destino como String (observe que o pacote java.lang. pode ser omitido)
10 Fragmentos de renderização do modelo Fragmentos de modelo (AJAX etc)
O Thymeleaf oferece a capacidade de renderizar apenas parte do modelo como resultado de sua execução:
fragment .
Essa pode ser uma ferramenta útil de componente. Por exemplo, ele pode ser usado em controladores executados em chamadas
AJAX que podem retornar fragmentos do layout de uma página que já está carregada no navegador (para atualizar a seleção, ative / desative os botões ...).
A renderização fragmentada pode ser obtida usando as especificações do trecho Thymeleaf: objetos que implementam a interface
org.thymeleaf.fragment.IFragmentSpec .
A mais comum dessas implementações é
org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec , que permite especificar um fragmento usando o seletor DOM da mesma maneira que os usados em
th: include ou
th: replace .
10.1 Definindo fragmentos em um bean de exibição
Os beans de exibição são os beans da classe
org.thymeleaf.spring4.view.ThymeleafView declarada no contexto do aplicativo (anotação
Bean, se você usar a configuração Java). Eles permitem que você especifique fragmentos da seguinte maneira:
@Bean(name="content-part") @Scope("prototype") public ThymeleafView someViewBean() { ThymeleafView view = new ThymeleafView("index");
Dada a definição acima de um bean, se o nosso controlador retornar uma
parte do conteúdo (o nome do bean acima) ...
@RequestMapping("/showContentPart") public String showContentPart() { ... return "content-part"; }
... O Thymeleaf retornará apenas o fragmento de
conteúdo do modelo de índice - o local provavelmente será aproximadamente o mesmo que
/WEB-INF/templates/index.html , após aplicar o prefixo e o sufixo. Assim, o resultado será completamente equivalente à especificação de
index :: content :
<!DOCTYPE html> <html> ... <body> ... <div th:fragment="content"> Only this div will be rendered! </div> ... </body> </html>
Observe também que, graças aos poderosos seletores de layout Thymeleaf, podemos selecionar um fragmento em um modelo sem nenhum atributo
th: fragment . Vamos usar o atributo
id , por exemplo:
@Bean(name="content-part") @Scope("prototype") public ThymeleafView someViewBean() { ThymeleafView view = new ThymeleafView("index");
10.2 Definindo fragmentos no valor de retorno do controlador
Em vez de declarar os
beans de exibição , os fragmentos podem ser definidos no controlador usando a sintaxe das
expressões de fragmento . Assim como nos atributos
th: insert ou
th: replace .
@RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: content"; }
Obviamente, todo o poder dos seletores DOM está disponível novamente, para que possamos selecionar nosso fragmento com base nos atributos HTML padrão, como
id = "content" :
@RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: #content"; }
E também podemos usar parâmetros como:
@RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: #content ('myvalue')"; }
11 Recursos avançados de integração
11.1 Integração com RequestDataValueProcessor
O Thymeleaf se integra perfeitamente à interface Spring
RequestDataValueProcessor . Essa interface permite interceptar URLs de link, URLs de formulário e valores de campo de formulário antes de gravá-los no resultado da marcação, além de adicionar transparentemente campos de formulário ocultos que incluem recursos de segurança, como: proteção contra CSRF (falsificação de solicitação entre sites) .
A implementação do
RequestDataValueProcessor pode ser facilmente configurada no contexto do aplicativo. Ele deve implementar a interface
org.springframework.web.servlet.support.RequestDataValueProcessor e ter
requestDataValueProcessor como o nome do bean:
@Bean public RequestDataValueProcessor requestDataValueProcessor() { return new MyRequestDataValueProcessor(); }
... e o Thymeleaf o usará da seguinte maneira:
- th: href e th: src chamam RequestDataValueProcessor.processUrl (...) antes de renderizar a URL
- th: action chama RequestDataValueProcessor.processAction (...) antes de renderizar o atributo action do formulário e, além disso, detecta quando esse atributo é aplicado à tag <form>, que em qualquer caso deve ser o único local e, neste caso, chama RequestDataValueProcessor.getExtraHiddenFields (...) ) e adiciona os campos ocultos retornados imediatamente antes da tag de fechamento </form>
- th: value chama RequestDataValueProcessor.processFormFieldValue (...) para desenhar o valor a que se refere, a menos que th: field esteja na mesma tag (neste caso, th: field terá cuidado)
- th: field chama RequestDataValueProcessor.processFormFieldValue (...) para desenhar o valor do campo ao qual se aplica (ou o corpo da tag, se for <textarea>)
Observe que existem muito poucos cenários nos quais você precisaria implementar explicitamente o RequestDataValueProcessor no seu aplicativo. Na maioria dos casos, ele será usado automaticamente pelas bibliotecas de segurança que você usa de forma transparente, por exemplo, o CSRF da Spring Security.11.1 Criando URIs para controladores
A partir da versão 4.1, o
Spring fornece a capacidade de criar links para controladores anotados diretamente das visualizações, sem a necessidade de conhecer os URIs para os quais esses controladores estão mapeados.
No Thymeleaf, isso pode ser alcançado usando a expressão
# mvc.url (...) , que permite definir métodos de controlador em letras maiúsculas da classe de controlador em que eles estão localizados, seguidos pelo nome do método. Isso é equivalente à função
spring: mvcUrlx (...) customizada no JSP.
Por exemplo, para:
public class ExampleController { @RequestMapping("/data") public String getData(Model model) { ... return "template" } @RequestMapping("/data") public String getDataParam(@RequestParam String type) { ... return "template" } }
O código a seguir criará referências de método:
<a th:href="${(#mvc.url('EC#getData')).build()}">Get Data Param</a> <a th:href="${(#mvc.url('EC#getDataParam').arg(0,'internal')).build()}">Get Data Param</a>
Você pode ler sobre esse mecanismo em
http://docs.spring.io/spring-framework/docs/4.1.2.RELEASE/spring-framework-reference/html/mvc.html#mvc-links-to-controllers- de visualizações12 Integração do Spring WebFlow
Os pacotes de integração Thymeleaf + Spring incluem integração com o Spring WebFlow (2.3+).
O WebFlow inclui alguns recursos do AJAX para renderizar fragmentos da página exibida quando determinados eventos (transições) são acionados e, para que o Thymeleaf possa rastrear essas solicitações do AJAX, precisaremos usar outra implementação do ViewResolver configurada da seguinte forma:
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring4.view.AjaxThymeleafViewResolver"> <property name="viewClass" value="org.thymeleaf.spring4.view.FlowAjaxThymeleafView" /> <property name="templateEngine" ref="templateEngine" /> </bean>
... e esse
ViewResolver pode ser configurado no
WebFlow ViewFactoryCreator como:
<bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator"> <property name="viewResolvers" ref="thymeleafViewResolver"/> </bean>
A partir daqui, você pode definir os modelos Thymeleaf nos seus estados de exibição:
<view-state id="detail" view="bookingDetail"> ... </view-state>
No exemplo acima,
bookingDetail é um modelo Thymeleaf especificado da maneira usual, compreensível para qualquer um dos resolvedores de modelos configurados no TemplateEngine.
12.2 Snippets AJAX no Spring WebFlow
Observe que isso explica apenas como criar fragmentos AJAX para uso com o Spring WebFlow. Se você não estiver usando o WebFlow, criar um controlador Spring MVC que responda a uma solicitação AJAX e retorne um pedaço de HTML é tão simples quanto criar qualquer outro controlador que retorne um modelo, com a única exceção de que você provavelmente retornará um fragmento como " main :: admin "do seu método de controle.O WebFlow permite definir a renderização por meio do AJAX com tags <render>, por exemplo:
<view-state id="detail" view="bookingDetail"> <transition on="updateData"> <render fragments="hoteldata"/> </transition> </view-state>
Esses fragmentos (neste caso,
dados do hotel ) podem ser uma lista de fragmentos separados por vírgula, indicados na marcação com
th: fragment :
<div id="data" th:fragment="hoteldata"> This is a content to be changed </div>
Lembre-se sempre de que esses trechos devem ter um atributo
id para que as bibliotecas Spring JavaScript em execução no navegador possam substituir a marcação.
Você também pode especificar tags <render> usando os seletores DOM:
<id do estado da visualização = "detalhe" view = "bookingDetail">
/>
</view-state>
... e isso significa que não há necessidade de
th: fragment :
<div id="data"> This is a content to be changed </div>
Quanto ao código que aciona a transição
updateData , ele se parece com o seguinte:
<script type="text/javascript" th:src="@{/resources/dojo/dojo.js}"></script> <script type="text/javascript" th:src="@{/resources/spring/Spring.js}"></script> <script type="text/javascript" th:src="@{/resources/spring/Spring-Dojo.js}"></script> ... <form id="triggerform" method="post" action=""> <input type="submit" id="doUpdate" name="_eventId_updateData" value="Update now!" /> </form> <script type="text/javascript"> Spring.addDecoration( new Spring.AjaxEventDecoration({formId:'triggerform',elementId:'doUpdate',event:'onclick'})); </script>