指南:胸腺+春天。 第三部分

第一部分
第二部分

7验证和错误消息


我们的大多数表单都应显示验证消息,以告知用户他所犯的错误。

Thymeleaf为此提供了一些工具: #fields对象中的多个函数, th:错误th:errorclass属性

7.1现场错误


让我们看看如果字段包含错误,如何为该字段设置特定的CSS类:

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

如您所见,函数#fields.hasErrors(...)接收字段表达式作为参数( datePlanted ),并返回一个布尔值,该值指示该字段是否存在任何验证错误。

我们还可以获取该字段的所有错误并重复它们:

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

除了迭代之外,我们还可以使用th:errors ,这是一个特殊的属性,它创建一个列表,其中包含指定选择器的所有错误,并用<br />分隔:

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

基于错误的CSS样式: th:errorclass

上面我们看到的示例很常见,如果此字段中有错误,则为输入表单设置CSS类,因此Thymeleaf提供了一个特殊的属性来准确执行: th:errorclass

应用于表单字段标签(输入,选择,文本区域...),它将从同一标签中的任何现有名称th:字段属性读取要检查的字段的名称 ,然后将指定的CSS类添加到标签(如果有)有任何相关的错误:

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

如果datePlanted中有错误,它将如下所示:

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

7.2所有错误


但是,如果我们想在表格中显示所有错误怎么办? 我们只需要请求具有常量' * '或' all '(等效)的#fields.hasErrors(...)#fields.errors(...)方法:

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

如以上示例所示,我们可以获取所有错误并对其进行迭代...

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

...并创建一个共享列表<br />:

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

最后,请注意, #fields.hasErrors('*')等效于#fields.hasAnyErrors() ,而#fields.errors('*')等效于#fields.allErrors() 。 使用您喜欢的语法:

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

7.3全局错误


Spring表单中存在第三种错误类型:全局错误。 这些错误与表单中的任何特定字段均不相关,但仍然存在。
Thymeleaf提供了访问这些错误的全局常量:

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

...以及等效的辅助方法#fields.hasGlobalErrors()#fields.globalErrors()

7.4在表格之外显示错误


表单验证错误也可以使用变量( $ {...... )而不是选择表达式( * {...... ))和支持该表单的组件名称的前缀显示在表单外部:

 <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丰富的错误对象


Thymeleaf可以接收具有属性fieldName (String), message (String)和global (boolean)的bean组件形式(而不是简单字符串)形式错误的信息。

可以使用#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这仍然是原型!


我们的应用程序已准备就绪。 但是,让我们再来看一下我们创建的.html页面...

使用Thymeleaf带来的最令人愉快的后果之一是,在将所有这些功能添加到HTML之后,我们仍然可以将该HTML用作原型(我们说这是Natural Template )。 让我们直接在浏览器中打开seedstartermng.html而不启动我们的应用程序:

图片

在这里! 这不是一个有效的应用程序,它不是真实的数据……但是它是由完全显示的HTML代码组成的完全正确的原型。

9转换服务


9.1配置


如前所述,Thymeleaf可以使用在应用程序上下文中注册的转换服务。 我们的应用程序配置类扩展了本机Spring WebMvcConfigurerAdapter帮助器 ,将自动注册一个转换服务,我们可以通过添加必要的格式化工具来对其进行配置。 让我们再看一下它的外观:

 @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双括号语法


转换服务可以轻松应用于将任何对象转换/格式化为字符串。 这是使用双括号表达式语法完成的:

  • 对于变量表达式: $ {{......}
  • 表达选择: * {{......}

因此,例如,给定整数到字符串转换器,它将逗号添加为千位分隔符,这是:

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

...应导致:

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

9.3表格使用


前面我们看到,每个th:字段属性将始终应用转换服务,因此:

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

...实际上相当于:

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

请注意,根据Spring的要求,这是将转换服务应用于使用单括号语法的表达式的唯一方案。

9.4 #conversions转换对象


#conversions转换实用程序对象使您可以在必要时手动启动转换服务:

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

该服务对象的语法为:

  • #conversions.convert(Object,Class) :将对象转换为指定的类
  • #conversions.convert(Object,String) :与上面相同,但目标类为String(请注意,可以省略java.lang。包)

模板模板片段的10个渲染片段(AJAX等)


Thymeleaf提供了仅由于执行模板而呈现部分模板的功能: fragment

这可能是有用的组件化工具。 例如,它可以在运行AJAX调用的控制器上使用,该控制器可以返回已经加载到浏览器中的页面布局的片段(以更新选择,启用/禁用按钮...)。

可以使用Thymeleaf片段规范(实现org.thymeleaf.fragment.IFragmentSpec接口的对象)来实现片段化渲染。

这些实现中最常见的是org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec ,它允许您使用DOM选择器指定片段,就像在th:includeth:replace中使用的那样

10.1在View Bean中定义片段


视图bean是在应用程序上下文中声明的org.thymeleaf.spring4.view.ThymeleafView类的bean(如果使用Java配置,则为Bean注释)。 它们允许您指定片段,如下所示:

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

给定上述bean的定义,如果我们的控制器返回一个content-part (上述bean的名称)...

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

... Thymeleaf将仅返回索引模板的内容片段-在应用前缀和后缀之后,其位置可能与/WEB-INF/templates/index.html大致相同。 因此,结果将完全等同于指定index :: content

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

还要注意,由于功能强大的Thymeleaf布局选择器,我们可以在模板中选择没有任何th:fragment属性的片段 。 让我们使用id属性,例如:

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

10.2在控制器的返回值中定义片段


除了声明视图bean之外 ,还可以使用片段表达式语法从控制器中定义片段 。 就像在th:insertth:replace属性中一样。

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

当然,DOM选择器的全部功能再次可用,因此我们可以基于标准HTML属性(例如id =“ content”)来选择片段:

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

我们还可以使用以下参数:

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

11种高级集成功能


11.1与RequestDataValueProcessor集成


Thymeleaf与Spring RequestDataValueProcessor接口无缝集成。 此接口使您可以在将链接URL,表单URL和表单域值写入标记结果之前对其进行拦截,以及透明地添加包含安全功能的隐藏表单域,例如:防止CSRF(跨站点请求伪造) 。

可以在应用程序上下文中轻松配置RequestDataValueProcessor的实现。 它应该实现org.springframework.web.servlet.support.RequestDataValueProcessor接口,并将requestDataValueProcessor作为bean名称:

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

...,Thymeleaf将按以下方式使用它:

  • th:hrefth:src在呈现URL之前调用RequestDataValueProcessor.processUrl(...)
  • th:操作在呈现表单的action属性之前调用RequestDataValueProcessor.processAction(...) ,此外,它还会检测何时将此属性应用于<form>标记(在任何情况下都应是唯一的位置),并且在这种情况下调用RequestDataValueProcessor.getExtraHiddenFields(... ),并在结束</ form>标记之前添加返回的隐藏字段
  • th:值调用RequestDataValueProcessor.processFormFieldValue(...)以绘制其引用的值,除非th:字段位于同一标记中(在这种情况下, th:字段将负责)
  • th:字段调用RequestDataValueProcessor.processFormFieldValue(...)以绘制其适用的字段的值(如果为<textarea>,则标记的主体)

请注意,在极少数情况下,您需要在应用程序中显式实现RequestDataValueProcessor 在大多数情况下,您透明使用的安全性库将自动使用它,例如,Spring Security的CSRF。

11.1建立到控制器的URI


从4.1版开始, Spring提供了直接从视图创建到带注释的控制器的链接的功能,而无需知道这些控制器映射到的URI。

在Thymeleaf中,可以使用表达式#mvc.url(...)来实现,该表达式允许您以控制器方法所在的控制器类的大写字母设置控制器方法,后跟方法名称。 这等效于JSP中的自定义spring:mvcUrlx(...)函数。

例如,用于:

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

以下代码将创建方法引用:

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

您可以在http://docs.spring.io/spring-framework/docs/4.1.2.RELEASE/spring-framework-reference/html/mvc.html#mvc-links-to-controllers-从观点

12 Spring WebFlow集成


Thymeleaf + Spring集成软件包包括与Spring WebFlow(2.3+)的集成。

WebFlow包含一些AJAX功能,用于在触发某些事件(过渡)时呈现显示页面的片段,并且Thymeleaf能够跟踪这些AJAX请求,我们将需要使用如下配置的另一个ViewResolver实现:

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

...,然后可以在WebFlow ViewFactoryCreator中将该ViewResolver配置为:

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

在这里,您可以在视图状态下定义Thymeleaf模板:

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

在上面的示例中, bookingDetail是以常规方式指定的Thymeleaf模板,对于TemplateEngine中配置的任何模板解析器都是可以理解的。

Spring WebFlow中的12.2 AJAX代码片段


请注意,这仅说明了如何创建用于Spring WebFlow的AJAX片段。 如果您不使用WebFlow,那么创建一个响应AJAX请求并返回HTML的Spring MVC控制器就像创建其他任何返回模板的控制器一样简单,唯一的例外是您很可能会返回“ main ”之类的片段。 :: admin ”,从您的控制器方法开始。

WebFlow允许您使用<render>标签通过AJAX定义呈现,例如:

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

这些片段(在本例中为hoteldata )可以是用逗号分隔的片段列表,该片段在标记中以th:fragment表示

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

始终记住,这些代码段必须具有id属性,以便浏览器中运行的Spring JavaScript库可以替换标记。

您还可以使用DOM选择器指定<render>标记:

<view-state id =“ detail” view =“ bookingDetail”>
/>

</ view-state>

...这意味着不需要:

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

至于触发updateData转换的代码,如下所示:

 <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/zh-CN437658/


All Articles