Guía: Thymeleaf + Spring. Parte 3

Primera parte
Segunda parte

7 Verificación y mensajes de error


La mayoría de nuestros formularios deben mostrar mensajes de validación para informar al usuario sobre los errores que ha cometido.

Thymeleaf ofrece varias herramientas para esto: varias funciones en el objeto #fields , th: errores y th: atributos de clase de error .

7.1 Errores de campo


Veamos cómo podemos establecer una clase CSS específica para un campo si contiene un error:

<input type="text" th:field="*{datePlanted}" th:class="${#fields.hasErrors('datePlanted')}? fieldError" /> 

Como puede ver, la función # fields.hasErrors (...) recibe la expresión de campo como un parámetro ( datePlanted ) y devuelve un valor booleano que indica si hay algún error de validación para este campo.

También podemos obtener todos los errores para este campo y repetirlos:

 <ul> <li th:each="err : ${#fields.errors('datePlanted')}" th:text="${err}" /> </ul> 

En lugar de iterar, también podríamos usar th: errors , un atributo especializado que crea una lista con todos los errores para el selector especificado, separados por <br />:

 <input type="text" th:field="*{datePlanted}" /> <p th:if="${#fields.hasErrors('datePlanted')}" th:errors="*{datePlanted}">Incorrect date</p> 

Estilo CSS basado en errores: th: errorclass

El ejemplo que vimos anteriormente, establecer la clase CSS para el formulario de entrada, si hay errores en este campo, es tan común que Thymeleaf ofrece un atributo especial para una ejecución precisa: th: errorclass .

Aplicado a la etiqueta de campo de formulario (input, select, textarea ...), leerá el nombre del campo a verificar desde cualquier nombre o atributo de campo existente en la misma etiqueta, y luego agregará la clase CSS especificada a la etiqueta, si tal campo tiene algún error relacionado:

 <input type="text" th:field="*{datePlanted}" class="small" th:errorclass="fieldError" /> 

Si hay errores en datePlanted , se verá así:

 <input type="text" id="datePlanted" name="datePlanted" value="2013-01-01" class="small fieldError" /> 

7.2 Todos los errores


Pero, ¿qué pasa si queremos mostrar todos los errores en el formulario? Solo necesitamos solicitar los métodos # fields.hasErrors (...) y # fields.errors (...) con las constantes ' * ' o ' all ' (que son equivalentes):

 <ul th:if="${#fields.hasErrors('*')}"> <li th:each="err : ${#fields.errors('*')}" th:text="${err}">Input is incorrect</li> </ul> 

Como en los ejemplos anteriores, podríamos obtener todos los errores e iterar sobre ellos ...

 <ul> <li th:each="err : ${#fields.errors('*')}" th:text="${err}" /> </ul> 

... y también crea una lista compartida <br />:

 <p th:if="${#fields.hasErrors('all')}" th:errors="*{all}">Incorrect date</p> 

Finalmente, tenga en cuenta que # fields.hasErrors ('*') es equivalente a # fields.hasAnyErrors () , y # fields.errors ('*') es equivalente a # fields.allErrors () . Use la sintaxis que prefiera:

 <div th:if="${#fields.hasAnyErrors()}"> <p th:each="err : ${#fields.allErrors()}" th:text="${err}">...</p> </div> 

7.3 Errores globales


Hay un tercer tipo de error en la forma Spring: errores globales. Estos son errores que no están asociados con ningún campo específico en el formulario, pero que aún existen.
Thymeleaf ofrece una constante global para acceder a estos errores:

 <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 así como los métodos auxiliares equivalentes # fields.hasGlobalErrors () y # fields.globalErrors () :

7.4 Mostrar errores fuera de los formularios


Los errores de validación de formularios también se pueden mostrar fuera de los formularios utilizando variables ( $ {...} ) en lugar de seleccionar expresiones ( * {...} ) y un prefijo para el nombre del componente que admite el formulario:

 <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 error enriquecidos


Thymeleaf ofrece la posibilidad de obtener información sobre errores de formulario en forma de componentes de bean (en lugar de cadenas simples) con los atributos fieldName (String), message (String) y global (boolean).

Estos errores se pueden obtener utilizando el método de utilidad # 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 ¡Esto sigue siendo un prototipo!


Nuestra aplicación está lista. Pero echemos otro vistazo a la página .html que creamos ...

Una de las consecuencias más agradables de trabajar con Thymeleaf es que después de todas estas funciones que hemos agregado a nuestro HTML, aún podemos usar este HTML como prototipo (decimos que se trata de una plantilla natural ). Abramos seedstartermng.html directamente en nuestro navegador sin iniciar nuestra aplicación:

imagen

Aqui esta! Esta no es una aplicación que funcione, no son datos reales ... pero es un prototipo completamente correcto compuesto de código HTML perfectamente visualizado.

9 El servicio de conversión


9.1 Configuración


Como se explicó anteriormente, Thymeleaf puede usar el Servicio de transformación registrado en el contexto de la aplicación. Nuestra clase de configuración de aplicaciones, que expande el asistente nativo Spring WebMvcConfigurerAdapter , registrará automáticamente un servicio de conversión que podemos configurar agregando las herramientas de formato necesarias. Veamos cómo se ve de nuevo:

 @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 Sintaxis de doble paréntesis


El servicio de conversión se puede aplicar fácilmente para convertir / formatear cualquier objeto en una cadena. Esto se hace usando la sintaxis de expresión de paréntesis doble:

  • Para expresiones variables: $ {{...}}
  • Para expresar una elección: * {{...}}

Entonces, por ejemplo, dado el convertidor de entero a cadena, que agrega comas como un separador de miles, esto es:

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

... debería resultar en:

 <p>1234567890</p> <p>1,234,567,890</p> 

9.3 Uso en formularios


Vimos anteriormente que cada atributo th: field siempre aplicará un servicio de transformación, así que esto:

 <input type="text" th:field="*{datePlanted}" /> 

... en realidad equivalente a:

 <input type="text" th:field="*{{datePlanted}}" /> 

Tenga en cuenta que, según los requisitos de Spring, este es el único escenario en el que el servicio de transformación se aplica a expresiones que utilizan la sintaxis de paréntesis único.

9.4 # objeto de conversión de conversiones


El objeto de utilidad de conversión #conversiones le permite iniciar manualmente el servicio de conversión cuando sea necesario:

 <p th:text="${'Val: ' + #conversions.convert(val,'String')}">...</p> 

La sintaxis para este objeto de servicio es:

  • # converssions.convert (Object, Class) : convierte el objeto a la clase especificada
  • # converssions.convert (Object, String) : igual que el anterior, pero con la clase de destino como String (tenga en cuenta que el paquete java.lang. puede omitirse)

10 Fragmentos de representación de la plantilla Fragmentos de plantilla (AJAX, etc.)


Thymeleaf ofrece la capacidad de representar solo una parte de la plantilla como resultado de su ejecución: fragmento .

Esta puede ser una herramienta útil de componente. Por ejemplo, se puede usar en controladores que se ejecutan en llamadas AJAX que pueden devolver fragmentos del diseño de una página que ya está cargada en el navegador (para actualizar la selección, activar / desactivar los botones ...).

La representación fragmentada se puede lograr utilizando especificaciones de fragmentos de Thymeleaf: objetos que implementan la interfaz org.thymeleaf.fragment.IFragmentSpec .

La más común de estas implementaciones es org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec , que le permite especificar un fragmento utilizando el selector DOM, al igual que los utilizados en th: include o th: replace .

10.1 Definición de fragmentos en un bean View


Los beans de vista son beans de la clase org.thymeleaf.spring4.view.ThymeleafView declarada en el contexto de la aplicación (anotación de Bean si usa la configuración de Java). Le permiten especificar fragmentos de la siguiente manera:

 @Bean(name="content-part") @Scope("prototype") public ThymeleafView someViewBean() { ThymeleafView view = new ThymeleafView("index"); // templateName = 'index' view.setMarkupSelector("content"); return view; } 

Dada la definición anterior de un bean, si nuestro controlador devuelve una parte de contenido (el nombre del bean anterior) ...

 @RequestMapping("/showContentPart") public String showContentPart() { ... return "content-part"; } 

... Thymeleaf devolverá solo el fragmento de contenido de la plantilla de índice, cuya ubicación probablemente sea aproximadamente la misma que /WEB-INF/templates/index.html , después de aplicar el prefijo y el sufijo. Por lo tanto, el resultado será completamente equivalente a especificar index :: content :

 <!DOCTYPE html> <html> ... <body> ... <div th:fragment="content"> Only this div will be rendered! </div> ... </body> </html> 

También tenga en cuenta que gracias a los potentes selectores de diseño Thymeleaf, podemos seleccionar un fragmento en una plantilla sin ningún atributo th: fragment . Usemos el atributo id , por ejemplo:

 @Bean(name="content-part") @Scope("prototype") public ThymeleafView someViewBean() { ThymeleafView view = new ThymeleafView("index"); // templateName = 'index' view.setMarkupSelector("#content"); return view; } 

10.2 Definición de fragmentos en el valor de retorno del controlador


En lugar de declarar beans de vista , los fragmentos se pueden definir desde el controlador utilizando la sintaxis de expresiones de fragmentos . Al igual que en los atributos th: insert o th: replace .

 @RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: content"; } 

Por supuesto, toda la potencia de los selectores DOM vuelve a estar disponible, por lo que podemos seleccionar nuestro fragmento en función de los atributos HTML estándar, como id = "content" :

 @RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: #content"; } 

Y también podemos usar parámetros como:

 @RequestMapping("/showContentPart") public String showContentPart() { ... return "index :: #content ('myvalue')"; } 

11 características de integración avanzada


11.1 Integración con RequestDataValueProcessor


Thymeleaf se integra perfectamente con la interfaz Spring RequestDataValueProcessor . Esta interfaz le permite interceptar URL de enlace, URL de formulario y valores de campo de formulario antes de escribirlos en el resultado de marcado, así como agregar de forma transparente campos de formulario ocultos que incluyen características de seguridad, tales como: protección contra CSRF (falsificación de solicitud entre sitios) .

La implementación de RequestDataValueProcessor se puede configurar fácilmente en el contexto de la aplicación. Debe implementar la interfaz org.springframework.web.servlet.support.RequestDataValueProcessor y tener requestDataValueProcessor como el nombre del bean:

 @Bean public RequestDataValueProcessor requestDataValueProcessor() { return new MyRequestDataValueProcessor(); } 

... y Thymeleaf lo usará de la siguiente manera:

  • th: href y th: src llaman a RequestDataValueProcessor.processUrl (...) antes de representar la URL
  • th: action llama a RequestDataValueProcessor.processAction (...) antes de representar el atributo de acción del formulario, y además detecta cuándo se aplica este atributo a la etiqueta <form>, que en cualquier caso debería ser el único lugar, y en este caso llama a RequestDataValueProcessor.getExtraHiddenFields (... ) y agrega los campos ocultos devueltos inmediatamente antes de la etiqueta de cierre </form>
  • th: value llama a RequestDataValueProcessor.processFormFieldValue (...) para dibujar el valor al que hace referencia, a menos que th: field esté en la misma etiqueta (en este caso th: field se ocupará)
  • th: el campo llama a RequestDataValueProcessor.processFormFieldValue (...) para dibujar el valor del campo al que se aplica (o el cuerpo de la etiqueta si es <textarea>)

Tenga en cuenta que hay muy pocos escenarios en los que necesite implementar explícitamente RequestDataValueProcessor en su aplicación. En la mayoría de los casos, será utilizado automáticamente por las bibliotecas de seguridad que utilice de forma transparente, por ejemplo, CSRF de Spring Security.

11.1 Creación de URI para controladores


A partir de la versión 4.1, Spring proporciona la capacidad de crear enlaces a controladores anotados directamente desde las vistas, sin la necesidad de conocer los URI a los que se asignan estos controladores.

En Thymeleaf, esto se puede lograr utilizando la expresión # mvc.url (...) , que le permite establecer métodos de controlador en mayúsculas de la clase de controlador en la que se encuentran, seguido del nombre del método. Esto es equivalente a la función personalizada spring: mvcUrlx (...) en JSP.

Por ejemplo, para:

 public class ExampleController { @RequestMapping("/data") public String getData(Model model) { ... return "template" } @RequestMapping("/data") public String getDataParam(@RequestParam String type) { ... return "template" } } 

El siguiente código creará referencias de métodos:

 <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> 

Puede leer sobre este mecanismo en http://docs.spring.io/spring-framework/docs/4.1.2.RELEASE/spring-framework-reference/html/mvc.html#mvc-links-to-controllers- desde vistas

12 Integración Spring WebFlow


Los paquetes de integración Thymeleaf + Spring incluyen integración con Spring WebFlow (2.3+).

WebFlow incluye algunas características de AJAX para representar fragmentos de la página mostrada cuando se activan ciertos eventos (transiciones), y para que Thymeleaf pueda rastrear estas solicitudes de AJAX, necesitaremos usar otra implementación de ViewResolver configurada de la siguiente manera:

 <bean id="thymeleafViewResolver" class="org.thymeleaf.spring4.view.AjaxThymeleafViewResolver"> <property name="viewClass" value="org.thymeleaf.spring4.view.FlowAjaxThymeleafView" /> <property name="templateEngine" ref="templateEngine" /> </bean> 

... y luego este ViewResolver se puede configurar en WebFlow ViewFactoryCreator como:

 <bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator"> <property name="viewResolvers" ref="thymeleafViewResolver"/> </bean> 

Desde aquí puede definir plantillas Thymeleaf en el estado de su vista:

 <view-state id="detail" view="bookingDetail"> ... </view-state> 

En el ejemplo anterior, bookingDetail es una plantilla Thymeleaf especificada de la manera habitual, comprensible para cualquiera de los solucionadores de plantillas configurados en TemplateEngine.

12.2 Fragmentos de AJAX en Spring WebFlow


Tenga en cuenta que esto solo explica cómo crear fragmentos AJAX para usar con Spring WebFlow. Si no está utilizando WebFlow, crear un controlador Spring MVC que responda a una solicitud AJAX y devuelva un fragmento de HTML es tan simple como crear cualquier otro controlador que devuelva una plantilla, con la única excepción de que es probable que devuelva un fragmento como " main :: admin "desde su método de controlador.

WebFlow le permite definir la representación a través de AJAX con etiquetas <render>, por ejemplo, así:

 <view-state id="detail" view="bookingDetail"> <transition on="updateData"> <render fragments="hoteldata"/> </transition> </view-state> 

Estos fragmentos (en este caso hoteldata ) pueden ser una lista de fragmentos separados por comas indicados en el marcado con th: fragment :

 <div id="data" th:fragment="hoteldata"> This is a content to be changed </div> 

Recuerde siempre que estos fragmentos deben tener un atributo de identificación para que las bibliotecas Spring JavaScript que se ejecutan en el navegador puedan reemplazar el marcado.

También puede especificar etiquetas <render> utilizando selectores DOM:

<view-state id = "detail" view = "bookingDetail">
/>

</view-state>

... y eso significa que no hay necesidad de th: fragment :

 <div id="data"> This is a content to be changed </div> 

En cuanto al código que desencadena la transición updateData , se ve así:

 <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> 

Source: https://habr.com/ru/post/437658/


All Articles