Thymeleaf总结

SpringBoot频繁报错标签未闭合

1、开发 配置

 #解析非严格的html5
spring.thymeleaf.mode=LEGACYHTML5  ----------1
#调试的时候关闭缓存,为了开发方便同时关闭缓存。修改页面不需要重启程序
spring.thymeleaf.cache=false--------------2

2、使用Thymeleaf3.0  速度更快

 <properties>
         <thymeleaf.version>3.0.0.RELEASE</thymeleaf.version>
         <thymeleaf-layout-dialect.version>2.0.0</thymeleaf-layout-dialect.version>
 	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 	<java.version>1.8</java.version>
 </properties>
少个/或者没用闭合总是出现解析错误,默认是严格的xhtml,对于标签的无值属性也不友好。

SpringBoot href与src

<script type="text/javascript" th:src="@{/js/jquery.validate.min.js}"></script>  href没用
<script type="text/javascript" th:href="@{/js/message.js}"></script>  src没用

不知道为什么头疼一直404。

以下来自网上解释

href 表示超文本引用(hypertext reference),在 link和a 等元素上使用。src 表示来源地址,在 img、script、iframe 等元素上。src 的内容,是页面必不可少的一部分,是引入。href 的内容,是与该页面有关联,是引用。区别就是,引入和引用。

src和href之间存在区别,能混淆使用。src用于替换当前元素,href用于在当前文档和引用资源之间确立联系。

src是source的缩写,指向外部资源的位置,指向的内容将会嵌入到文档中当前标签所在位置;在请求src资源时会将其指向的资源下载并应用到文档内,例如js脚本,img图片和frame等元素。

<script src ="js.js"></script>

当浏览器解析到该元素时,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等元素也如此,类似于将所指向资源嵌入当前标签内。这也是为什么将js脚本放在底部而不是头部。

href是Hypertext Reference的缩写,指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,如果我们在文档中添加

<link href="common.css" rel="stylesheet"/>

那么浏览器会识别该文档为css文件,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用link方式来加载css,而不是使用@import方式。

标准表达式语法


但还有更多的类型和有趣的细节,我们都还不知道,下面我们首先来一个标准表达式的快速总结:

  • 简单表达式
    • 变量表达式:${...}
    • 选定变量表达式:*{...}
    • 信息表达式:#{...}
    • 链接表达式:@{...}
    • 片段表达式:~{...}

  • 字面值
    • 文本值:'one text','Another one!',...
    • 数值:1,34,3.0,12.3...
    • 布尔值:true,false
    • Null值:null
    • 标记符号值(token):one,sometext,main,...(?)

  • 操作符
    • 字符串连接:+
    • 文本替换:|The name is ${name}|
  • 算数运算符:
    • 基本操作符:+,-,*,/,%
    • 取负值(一元运算符):-
  • boolean运算符:
    • 基本二元操作符:and,or
    • 一元操作符:!,not
  • 比较和判断:
    • 比较运算符:>,<,>=,<=,(gt,le,ge,le)
    • 判断相等运算符:==,!=(eq,ne)
  • 条件运算符
    • if-then:(if)?(then)
    • if-then-else:(if)?(then):(else)
    • default:(value)?:(defaultvalue)
  • 特殊标记
    • 无操作:_

所有这些还可以合并嵌套使用:

  • 专用

  • ${x}:返回一个Thymeleaf的context中存储的变量或request的属性。
  • ${param.x} 返回request的参数x(可以为复合值)
  • ${session.x} 返回session的属性x
  • ${application.x} 返回一个servlet上下文的属性x

就在执行前,一个特殊的变量被设置为包含了Context和WebContext的全context对象(即所有实现了IContext的对象),被称为执行信息(execInfo),这个变量有两个您可以从模板中使用的数据。

  • 模板名:#{execInfo.templateName},一个引擎执行时的特定名称,对应正在执行的模板。
  • 当前的日期时间:#{execInfo.now},一个Calender对象,对应引擎开始执行的时刻值。

  • 基本对象表达式 
  • 注:当使用OGNL表达式的context变量的时候,可以使用更方便的表达方式。这些对象也是用来#符号:
  • #ctx: context对象
  • #vars:context属性
  • #locale:context本地化信息
  • #request:(仅限WebContext) HttpServletRequest对象
  • #response:(仅限WebContext) HttpServletResponse对象
  • #session:(仅限WebContext)HttpSession对象
  • #servletContext:(仅限WebContext)ServletContext对象

  • 工具对象
  • 除了基本对象,Thymeleaf还为我们提供了一套实用对象,可以帮助我们在执行表达式中解决一下常见的任务:
  • #execInfo:正在处理的模板信息
  • #messages:获取外部信息的内部变量的一个实用方法,同时也可以用#{...}获取
  • #uris:针对URL或URI进行一些转码的方法
  • #conversions:根据配置执行一些转换方法
  • #dates:针对java.util.Date对象的实用方法:包括日期格式化,日期提取等。
  • #calendars:与#dates相似,但针对的是java.util.Calendar对象。
  • #numbers:针对numeric对象格式化的实用方法
  • #strings:针对String对象的实用方法,包括包含,判断起始,前/后追加等方法
  • #objects:针对object类的一般实用方法
  • #boolean:针对boolean运算的一些实用方法
  • #arrays:针对数组的实用方法
  • #lists:针对list的实用方法
  • #sets:针对set的实用方法
  • #map:针对map的实用方法
  • #aggregates:在数组或集合中创建聚合的一些实用方法
  • #ids:用于处理可能重复的标识属性的使用方法,例如,作为迭代的变量。

  • UR

由于其重要性,URL是web应用程序模板的一等公民,Thymeleaf的标准方言都为它定义了特殊语法:@{...}

他有不同类型的网址:

  • 绝对地址,比如:https://niufennan.github.io/
  • 相对地址,可以有如下方式:
    • 相对于页的,比如:user/login.html
    • 相对于上下午的,比如:/itemdetails?d=3(将自动添加服务器的上下文名称)
    • 相对于服务器的,比如:~/billing/processInvoice(可以在同一服务器中的不同上下文(即application)中使用)
    • 相对于协议的,比如://cdn.bootcss.com/jquery/2.2.3/jquery.min.js

真正将这些表达式转换并输出为URL的工具,是一个注册在ITemplateEngine中的一个org.thymeleaf.linkbuilder.ILinkBuilder接口的实现类。

Thymeleaf可以在任何情况下处理绝对地址,但相应的,也会要求你给予一个实现了IWebContext接口的context对象,他包含了一些创建链接需要的来自Http请求的相关信息。

注意:

可以对url的参数使用表达式(比如orderId=${o.id}),url上所需的编码工作,也会自动执行。如果需要多个参数,用逗号(,)分开即可如:(@{/order/process(execId=${execId},execType='FAST')})网络路径中也可以使用变量模板,如:@{/order/{orderId}/details(orderId=${orderId})}相对URL使用/开始,如/order/details,将自动适应上下文的名词前缀。如果不清楚cookie是否被启用,一个jsessionid=...的后缀可能被加入到url中,用于回话保存,这就是所谓的URL重写。Thymeleaf允许利用Servlet的api为每一个url,使用response.encoding来扩展重写过滤器。th:href允许有一个静态工作的url配置在模板中,这样,我们的模板即使在不工作时,仍然可以通过浏览器互相连接。

Thymeleaf可以使用双大括号的方式,为变量${...}和选择*{...}表达式通过配置转换服务进行数据转换在thymeleaf-spring3和thymeleaf-spring4有一整套基于Spring转换服务的Thymeelaf转换服务配置,因此他可以自动实现${{}}和*{{}}的服务。

增加属性

Thymeleaf还提供了th:attrappend和th:attrprepend属性,用于为属性之前或之后增加值

比如,你可能想在一个按钮的现有css类的基础上在新增一个css类,将会非常容易:

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

如果你对cssStyle变量的值为warning,那么输出将为:

<input type="button" value="点击" class="btn warning" />

因为样式表使用的如此频繁,所以标准方言中还有两个附加属性,th:classappend和th:styleappend属性,用于追加一个class类或者一段样式表而不改变现有内容:

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

导入文件

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
  <body>
    <div th:fragment="copy">
      &copy; 头文件
    </div>
  </body>
</html>

上面定义了一个叫copy的代码片段,我们能通过th:insert和th:replace属性(还有th:include,但Thymeleaf3.*版本不推荐使用),很容易的将他导入到首页中:

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

注意一点,th:insert使用的是一个片段表达式(~{...}),或片段中一个更具体的表达式。但在前者的情况下(简单片段表达式),比如就像上边的代码,~{...}是可选的,所以如下班的代码是等价的:

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

片段语法说明

判断表达式语法非常简单,有三种不同的格式:

  • 模板名::dom选择将导入模板名所指定的代码片段到dom选择器中。

    • 注意:dom选择器可以仅仅是一个片段的名字,所以可以指定一下非常简单的名字,如:footer:copy或更简单的。
    • dom选择器语法类似于XPath或css选择器,更多内容见附录C。
  • 直接使用模板名将此模板对应的完整的代码导入。
  • 注意:此时由th:insert和th:replace标签导入的模板必须在当前的模板引擎下的模板解释器可以分辨。
  • ::dom选择器this:dom选择器导入与之相同的模板。
  • 在上面的格式中,模板名dom选择器都可以使用任何表达式的结果来表示,比如:

th:insert和th:replace的不同点(以及th:include)

几个属性的区别(th:insert,th:replace和th:include(3.*版本不推荐使用)):

  • th:insert是将th:fragment标签的内容纳入宿主标签
  • th:replace是使用th:fragment标签替换宿主标签
  • th:include与th:insert类似,但是他插入的是片段的内容,而不是片段

模板断言

th:assert属性可以定义一个用逗号分隔的表达式,用来为每一个条件做出评估,以判断是否产生异常。

内联表达式

表达式内联
虽然标准方言的标签属性几乎可以做任何事,但在有些情况下,我们可以直接使用HTML文本来书写表达式,例如,你可以这样写:
<p>Hello, [[${session.user.name}]]!</p>
他和这样写是等价的:
<p>Hello, <span th:text="${session.user.name}">张三</span>!</p>
[[...]]和[(...)]这种表达式在Thymeleaf中叫做内联表达式,你可以使用任何形式的表达式,包括th:text或th:utext属性
需要注意的是,[[...]]对应的是th:text,[(...)]对应的是th:utext,所以变量msg=这个<b>真棒</b>,加入使用片段:
<p>信息值为: "[(${msg})]"</p>
输出结果为:
<p>信息值为: 这个<b>真棒</b></p>
而如果这样:
<p>信息值为: "[[${msg}]]"</p>
则输出结果为:
<p>信息值为: 这个&lt;b&gt;真棒&lt;/b&gt;</p>
需要注意的是,文本内联在body内的每个标记的都默认激活,所以我们可以直接使用。

内联还是自然模板
那么现在你可能要问了:为什么不从一开始就使用内联表达式呢,他的代码量比属性少多了。

嗯,这是因为,你可能发现内联很好用,但你要永远记住,内联表达式在你的html中是完全按照文字打开显示的,所以,你就无法用他作为原型了。

比如,若果不适用内联,直接作为静态打开将显示:

Hello,张三
而使用内联后:

Hello, [[${session.user.name}]]!
在设计方面的区别还是很明显。

禁用内联
这个机制还是可以禁用的,比如有时候[[...]]或[(...)]是作为内容输出的。对于这点,我们可以使用th:inline="none";

<p th:inline="none">一个二维数组: [[1, 2, 3], [4, 5]]!</p>
执行结果为:

<p>一个二维数组: [[1, 2, 3], [4, 5]]!</p>
内联文本
内联文本与刚刚我们看到的内联表达式非常的相似,但它实际上有更多的功能,并且必须显式启用:th:inline="text"

内联文本不仅允许我们使用与刚刚看到相同的表达式形式,但其实他的处理方式比较像文本模板处理模式,即可以在里边处理文本模板逻辑,而不仅仅是一个输出的表达式。

我们将看在下一章文本模板模式中看到更多内容。

内联JavaScript
内联JavaScript可以更好的在HTML模板模式下整合JavaScript的脚本块。

就像内联文本一样,这实际上是相当于它们在JavaScript模板模式下处理脚本内容,执行的是文本模板模式的功能(下一章介绍),这一章将着重介绍如何在JavaScript块中使用Thymeleaf表达式。

这种模式必须明确声明启用 th:inline="javascript":
<script th:inline="javascript">
    ...
    var username = [[${session.user.name}]];
    ...
</script>

将输出结果为:
<script th:inline="javascript">
    ...
    var username = "张\"老\"三";
    ...
</script>
这里有两点需要注意:

首先,内联JavaScrip不但能输出普通文字,还会队内容自动进行转义和JavaScript的编码,所以输出的结果一般会非常符合JavaScript的要求。

其次,如果我们不想进行转义,则使用[(...)],如:
<script th:inline="javascript">
    ...
    var username = [(${session.user.name})];
    ...
</script>
返回结果为:

<script th:inline="javascript">
    ...
    var username = 张"老"三;
    ...
</script>
很明显,这是一段错误的代码,但有事我们就是需要一些非转义的东西,所以如果想通过内联来构筑脚本,手头最好有这个工具。

JavaScript的自然模板
上述的内联JavaScript机制要不仅仅运用JavaScript特性的表达式更为有效。

举例来说,我们可以将内联表达式使用JavaScript方式注释,如:
<script th:inline="javascript">
    ...
    var username = /*[[${session.user.name}]]*/ "张三";
    ...
</script>
Thymeleaf将忽略注释和分号之前的一切("张三"),所以他的执行结果和不使用注释包装一模一样:
<script th:inline="javascript">
    ...
    var username = "张\"老\"三";
    ...
</script>
但是仔细看看这个模板代码:
<script th:inline="javascript">
    ...
    var username = /*[[${session.user.name}]]*/ "张三";
    ...
</script>

它是一段完全正确的JavaScript代码,当在浏览器中直接打开的时候,它还是会正确的执行,并几乎和在服务器执行的结果一样。

所以这是一段JavaScript自然模板。

内联执行的高级方式和JavaScript序列化
Thymeleaf的内联执行是非常智能的,它不限于字符串,Thymeleaf会正确的创建JavaScript中以下的各种类型的对象:

字符串
数值
布尔型
数组
集合
Map
JavaBean
比如说,下面的代码:
<script th:inline="javascript">
    ...
    var user = /*[[${session.user}]]*/ null;
    ...
</script>

${session.user}是一个User对象,执行后的结果为

<script th:inline="javascript">
    ...
    var user = {'id':3,'age':27,'name':'张三'};
    ...
</script>
这个JavaScript完成序列化方式是通过org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer接口实现,可以在当前模板引擎中配置标准方言使用的序列化方式实例。

这个JS的序列化机制的默认是查询classpath,如果有JackSon库则使用它完成序列化,如果没有,他还内置了一个序列化工具,可以涵盖大多数的情况,但不太灵活。

内联CSS
Thymeleaf也可以使用内联CSS标签,像这样:
<style th:inline="css">
  ...
</style>
举个例子,需要为两个变量需要设置为两个不同的字符串值:

classname='main elems'
align='center'
就可以这样使用:
<style th:inline="css">
    .[[${classname}]] {
      text-align: [[${align}]];
    }
</style>
返回结果

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

注意一点,CSS内联也象JavaScript的一样,具有一定转换能力。具体说就是象[[${classname}]]输出的时候会自动转换,所以上文中的 classname='main elems'被转换为main\ elems

高级功能:CSS自然模板等
同JavaScript一样,内联CSS样式也可以静态或动态展示,即可以使用注释手段的方式实现自然模板。比如:
<style th:inline="css">
    .main\ elems {
      text-align: /*[[${align}]]*/ left;
    }
</style>

文本语言模板模式
文本语法
Thymeleaf有三种文本语言模板模式(Text,JavaScript,CSS),它们与标记语言模板模式有一些区别。

文本模板模式与标记语言模板模式的一个主要区别是,在文本模板中,没有标记,所以也就没有办法以属性形式插入逻辑标签,所以,我们必须依靠其它机制插入逻辑。

这些机制最基本的方式,就是我们前边已经详细介绍过的内联。内联语法是在文本语言模板模式中输出结果的最简单的表达式。用一个完整的电子邮件的模板文件作为例子:

[(${name})] 您好,
请在附件中查看您所要求的报告
名字为: "[(${report.name})]".
此致
    敬礼
A方项目经理 张三.
注意上面这个完整有效的Thymeleaf文本模板中,没有任何标记,但它完全可以正常执行。所以我们不应再标记语言模板模式中内联文本语言模板,而应直接在文本模板模式中直接执行。

而为了支持比单纯输出更复杂的逻辑,我们需要一个基于非标记的新语法形式:
[# th:each="item : ${items}"]
  - [(${item})]
[/]
它实际上是一个简写版:
[#th:block th:each="item : ${items}"]
  - [#th:block th:utext="${item}" /]
[/th:block]
[#th:block th:each="item : ${items}"]
  - [#th:block th:utext="${item}" /]
[/th:block]
注意这个新语法是基于元素(element,即加工后的标签)的,注意定义形式为[#element]而不是.元素和标签类似,使用[#element]和[/element]来确定一个封闭区间。还可以使用自关闭模式[#element.../]

标准方言的处理器实际上跟只可以使用一个元素,就是之前介绍过的th:block,虽然在自定义方言中可以扩展并创建新的元素。另外,th:block元素([#th:block ...]...[/th:block])可以直接缩写,([# ...]...[/]),所以上边的代码相当于:
[# th:each="item : ${items}"]
  - [# th:utext="${item}" /]
[/]

当然[#th:utext="${item}"]相当于一个内联转移表达式,所以我们也可以在此使用,用来减少代码,所以,最终我们看到的代码为:
[# th:each="item : ${items}"]
  - [(${item})]
[/]
注意文本模式语法要求必须为全平衡元素(即没有非闭合标签)和属性,所以,它与html比,更像XML的模式。

下面一个更完整的文本模板模式例子,一个电子邮件的模板:

[(${customer.name})],您好:
这是我们的产品列表:
[# th:each="prod : ${products}"]
   - [(${prod.name})]  价格为: [(${prod.price})] 元/kg
[/]

执行后输出的结果为:

张先生,您好:
这是我们的产品列表:
[# th:each="prod : ${products}"]
   - 土豆  价格为: 2.30元/kg
   - 白菜  价格为: 1.20元/kg
   - 西瓜  价格为: 1.60元/kg
   - 红薯  价格为: 3.50元/kg
[/]
在举一个JavaScript模板模式的例子,在我们的HTML页面,需要调用一个greeter.js文件,它使用文本模板创建。注意,他不是的方式内嵌到HTML文件中,而是以一个单独的js文件作为模板处理:

var greeter = function() {
    var username = [[${session.user.name}]];
    [# th:each="salut : ${salutations}"]
      alert([[${salut}]] + " " + username);
    [/]
};

执行后的结果为:

var greeter = function() {
    var username = "张三";
    alert("Hello" + " " + username);
    alert("Ol\u00E1" + " " + username);
    alert("Hola" + " " + username);
};


元素属性转义
为了防止模板可能与其他的模板处理方式发生冲突(如文本模式内联在html模板中),Thymeleaf3.*的文本模式语法可以在元素的属性中使用转义

Text模板模式的属性将会使用HTML非转义字符
JavaScript模板模式的属性将会使用JavaScript非转义字符
CSS模板模式将会使用CSS非转义字符
所以,下边的代码将会正常的执行(注意其中的&lt)

[# th:if="${120&lt;user.age}"]
 真棒!
[/]
当然,在一个真实的模板中,&lt是个错误的语法,但如果在执行一个html模板的内联text的时候,它就要起作用了,因为在上边的代码中,我们不希望浏览器认为<user.age是一个开放的静态标签。

可扩展性
此语法有一个有点就是它和标记一样有扩展性。用户仍然可以使用自定义元素或属性来定义方言,以前缀的方式应用(可选),然后在文本模板模式中使用它们。

[#myorg:dosomething myorg:importantattr="211"]一些内容[/myorg:dosomething]

使用文本原型注释:添加代码
如果要为JavaScript和CSS模板模式(文本模板模式不可以)添加额外的代码,则在内联JavaScript中使用一个特殊的语法/*[+...+]*/,Thymeleaf在模板执行的时候讲自动取消此注释:

var x = 23;
/*[+
var msg  = '这段代码为后台输出';
+]*/
var f = function() {
...
执行结果为:

var x = 23;
var msg  = '这段代码为后台输出';
var f = function() {
...
可以在这段注释中添加表达式,并执行:

var x = 23;
/*[+
var msg  = 'Hello,'+[[${session.user.name}]];
+]*/
var f = function() {
...
使用文本原型注释:删除代码
同样的,也可以让Thymeleaf删除/*[-*/和/*-]*/之间的代码(三种文本模板模式均可):

var x = 23;
/*[- */
var msg  = '执行后就没有了';
/* -]*/
var f = function() {
...
或者在文本中使用:

...
/*[- 用户从会话中获得 -]*/
[(${session.user.name})] 您好!
...
自然JavaScript和Css模板
正如上一章看到的,JavaScript和CSS内联提供了在内敛表达式内使用JavaScript和CSS注释的可能,比如:

...
var username = /*[[${session.user.name}]]*/ "张三";
...
执行后为一个正常的JavaScript,结果为:

...
var username = "李四";
...
这种欺骗内联表达式的语法实际上可以在全部的文本模式语法中使用:

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


这个alert语句当模板直接在浏览器中打开是会显示出来,因为是个完全正确的JavaScript代码,而当模板在运行后,当用户为管理员的时候alert才会弹出,因为它执行后的结果为:

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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值