第二部分、其他(Thymeleaf官方文档翻译)

    技术2022-07-16  80

    原文:文档5-13节

    2. 设置属性值

    2.1 给任意属性赋值

    使用th:attr可以给任意标签进行属性赋值,举例如下:

    <form action="subscribe.html" th:attr="action=@{/subscribe}"> <fieldset> <input type="text" name="email" /> <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/> </fieldset> </form>

    <form>元素的action属性会设置为@{/subscribe}变量的值

    <input type=“submit”>元素的value属性会被设置为#{subscribe.submit}变量的值

    多个赋值操作用逗号隔开:

    <img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" /> 结果举例: <img src="/gtgv/images/gtvglogo.png" title="Logo de Good Thymes" alt="Logo de Good Thymes" />

    2.2 给指定的属性赋值

    th:*属性可以给特定的属性赋值,通过把*替换为指定的属性名来使用:

    <input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/> <form action="subscribe.html" th:action="@{/subscribe}">

    Thymeleaf支持很多HTML5的属性:

    ––––––th:abbrth:acceptth:accept-charsetth:accesskeyth:actionth:alignth:altth:archiveth:audioth:autocompleteth:axisth:backgroundth:bgcolorth:borderth:cellpaddingth:cellspacingth:challengeth:charsetth:citeth:classth:classidth:codebaseth:codetypeth:colsth:colspanth:compactth:contentth:contenteditableth:contextmenuth:datath:datetimeth:dirth:draggableth:dropzoneth:enctypeth:forth:formth:formactionth:formenctypeth:formmethodth:formtargetth:fragmentth:frameth:frameborderth:headersth:heightth:highth:hrefth:hreflangth:hspaceth:http-equivth:iconth:idth:inlineth:keytypeth:kindth:labelth:langth:listth:longdescth:lowth:manifestth:marginheightth:marginwidthth:maxth:maxlengthth:mediath:methodth:minth:nameth:onabortth:onafterprintth:onbeforeprintth:onbeforeunloadth:onblurth:oncanplayth:oncanplaythroughth:onchangeth:onclickth:oncontextmenuth:ondblclickth:ondragth:ondragendth:ondragenterth:ondragleaveth:ondragoverth:ondragstartth:ondropth:ondurationchangeth:onemptiedth:onendedth:onerrorth:onfocusth:onformchangeth:onforminputth:onhashchangeth:oninputth:oninvalidth:onkeydownth:onkeypressth:onkeyupth:onloadth:onloadeddatath:onloadedmetadatath:onloadstartth:onmessageth:onmousedownth:onmousemoveth:onmouseoutth:onmouseoverth:onmouseupth:onmousewheelth:onofflineth:ononlineth:onpauseth:onplayth:onplayingth:onpopstateth:onprogressth:onratechangeth:onreadystatechangeth:onredoth:onresetth:onresizeth:onscrollth:onseekedth:onseekingth:onselectth:onshowth:onstalledth:onstorageth:onsubmitth:onsuspendth:ontimeupdateth:onundoth:onunloadth:onvolumechangeth:onwaitingth:optimumth:patternth:placeholderth:posterth:preloadth:radiogroupth:relth:revth:rowsth:rowspanth:rulesth:sandboxth:schemeth:scopeth:scrollingth:sizeth:sizesth:spanth:spellcheckth:srcth:srclangth:standbyth:startth:stepth:styleth:summaryth:tabindexth:targetth:titleth:typeth:usemapth:valueth:valuetypeth:vspaceth:widthth:wrapth:xmlbaseth:xmllangth:xmlspace

    2.3 同时给多个属性赋值

    th:alt-title 会把 alt 和 title属性设置为相同的值th:lang-xmllang 会把 lang 和 xml:lang属性设置为相同的值

    下面的两种写法是等效的

    <img src="../../images/gtvglogo.png" th:src="@{/images/gtvglogo.png}" th:title="#{logo}" th:alt="#{logo}" /> <img src="../../images/gtvglogo.png" th:src="@{/images/gtvglogo.png}" th:alt-title="#{logo}" />

    2.4 添加和插入

    如果你想在某个属性值的后面添加一个值,或者在它前面插入一个值,那么可以使用th:attrappend 和th:attrprepend ,举个例子:

    <input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" /> 如果cssStyle变量等于warning,那么上面的模板最终结果是: <input type="button" value="Do it!" class="btn warning" />

    还有两个专用的语法:th:classappend 和 th:styleappend,举个例子:

    <tr class="row" th:classappend="${prodStat.odd}? 'odd'"> <tr style="color:black;" th:styleappend="${customStyle}">

    2.5 布尔属性

    在HTML和XHTML中都有布尔属性,但是表示方式不一样。如果属性值为true,那么在HTML中该属性值就是true,而在XHTML中该属性值为属性名,举例来说:

    <input type="checkbox" name="option2" checked /> <!-- HTML --> <input type="checkbox" name="option1" checked="checked" /> <!-- XHTML -->

    Thymeleaf支持根据条件来设置合适的值,即如果条件为true,那么属性值会根据文档类型来设置,如果条件为false,那么就不设置这个值:

    <input type="checkbox" name="active" th:checked="${user.active}" />

    下面是支持的布尔属性:

    –––th:asyncth:autofocusth:autoplayth:checkedth:controlsth:declareth:defaultth:deferth:disabledth:formnovalidateth:hiddenth:ismapth:loopth:multipleth:novalidateth:nowrapth:openth:pubdateth:readonlyth:requiredth:reversedth:scopedth:seamlessth:selected

    2.6 设置任意属性和其值

    thymeleaf提供了一个默认的属性处理器,它允许我们设置任何属性的值。

    <span th:whatever="${user.name}">...</span> 结果: <span whatever="John Apricot">...</span>

    2.7 HTML5友好型写法

    支持使用data-{prefix}-{name} 的语法来设置HTML5元素的属性,不需要开发人员使用th:*这样的命名空间的写法。

    <table> <tr data-th-each="user : ${users}"> <td data-th-text="${user.login}">...</td> <td data-th-text="${user.name}">...</td> </tr> </table>

    还有一种语法用于指定自定义标记:{prefix}-{name},它遵循W3C自定义元素规范。举个例子来说 th:block和th-block支持block元素。

    第三部分 遍历

    3 遍历

    3.1 遍历基础

    使用 th:each

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

    可遍历的对象: java.util.List、 java.util.Iterable、 java.util.Enumeration、java.util.Iterator、java.util.Map、任何数组、object(Object被看成是只包含自己的list)

    3.2 遍历状态

    当前遍历索引, 从0开始:index.当前遍历索引, 从1开始:count .被遍历对象包含的元素个数:size.当前遍历元素:current.表示当前遍历是偶数还是奇数. even/odd 布尔值.当前遍历元素是否是第一个元素:first 布尔值.当前遍历元素是否是最后一个元素: last 布尔值.

    使用方法:

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

    状态变量(在这个例子中是iterStat 变量)定义在遍历元素后面,用逗号隔开。

    如果你不想显式声明状态变量,那么Thymeleaf会自动为你创建一个被遍历对象名 + Stat后缀的状态变量:

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

    状态变量的作用域被限制在声明th:each的元素下。

    4 通过惰性加载技术优化性能

    某些时候我们会有这样的需求,就是当真正需要使用数据的时候,我们才从存放数据的地方(如数据库)获取数据。

    Thymeleaf提供了数据惰性加载的机制,要求上下文变量需要实现 ILazyContextVariable接口。可以使用Thymeleaf实现的LazyContextVariable 类来实现:

    context.setVariable( "users", new LazyContextVariable<List<User>>() { @Override protected List<User> loadValue() { return databaseRepository.findAllUsers(); } });

    然后在模板里我们这样使用:

    <ul th:if="${condition}"> <li th:each="u : ${users}" th:text="${u.name}">user name</li> </ul>

    当${condition}为true的时候Thymeleaf才会去获取真实的数据,否则users数据将不会被加载。


    第四部分 条件取值

    5 条件取值

    5.1 使用th:if 和 th:unless

    我们可能希望只有在if条件为真时,才显示代码模板。这时可以使用th:if。来看一下下面的模板:

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

    上面的HTML代码中<a>元素只有在th:if判断为真时,才会显示。上面模板最终输出的结果如下:

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

    那么有哪些条件下th:if判断为真呢?

    值不为空,if条件为真: If value is a boolean and is true.If value is a number and is non-zeroIf value is a character and is non-zeroIf value is a String and is not “false”, “off” or “no”If value is not a boolean, a number, a character or a String. 值为空,if条件为假

    和th:if相对,th:unless也可以用来条件判断。那么上面的代码就可以改成:

    <a href="comments.html" th:href="@{/comments(prodId=${prod.id})}" th:unless="${#lists.isEmpty(prod.comments)}">view</a>

    也就是说,<a>元素会一直显示,除非th:unless条件为真。

    5.2 使用th:switch

    和其他编程语言类似,Thymeleaf提供了switch条件判断:

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

    th:case="*"代表默认值


    第六部分 模板布局

    6. 模板布局

    6.1 引用代码片段

    6.1.1 定义和引用代码片段

    有时候我们想从其他的模板文件引入代码,这时候可以使用th:fragment来实现。

    首先创建一个/WEB-INF/templates/footer.html文件,然后文件内容如下:

    <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <body> <div th:fragment="copy"> © 2011 The Good Thymes Virtual Grocery </div> </body> </html>

    上面的代码定义了一个名为copy的代码片段。在其他HTML文件中,我们可以使用th:insert或者th:replace属性来引用这段代码(也可以使用th:include,但是在Thymeleaf3.0中不推荐使用):

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

    注意到~{footer :: copy}是一个fragment 表达式,代表结果在一个fragment里。fragment 表达式也可以简写:

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

    6.1.2 Fragment语法规范

    Fragment表达式有三种书写方式:

    "~{templatename::selector}" :从名为templatename的模板文件中,引用被selector选择器选中的代码片段。selector选择器的使用和CSS选择器很相似,它的低层使用了AttoParser parsing,详细内容请看这里Appendix C"~{templatename}" 引用模板名为template的全部内容"~{::selector}" or "~{this::selector}"引用同一个模板文件中的代码片段。当selector找不到匹配项时,模板引擎会搜索从模板根目录搜索,直到找到匹配项位置。 Fragment表达式还可以和其他的语法组合,构成功能更加丰富的语句: ```xml ```

    代码模板里可以使用任何th:*的属性,当代码模板插入到其他模板文件中时,所有的th:*属性都会被解析。

    6.1.3 不用th:fragment引用代码片段

    由于强大的 Markup Selectors的支持,我们可以不用th:fragment属性就可以引用代码片段,甚至是从其他没有使用Thymeleaf的应用里引用代码。看下面的例子:

    ... <div id="copy-section"> © 2011 The Good Thymes Virtual Grocery </div> ...

    我们可以通过引用div的id属性来引用这片代码,选择器的写法和CSS选择器一样:

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

    6.1.4 th:insert和th:replace的不同

    th:insert 插入标有th:insert的元素,作为其子元素.th:replace 替换标有th:replace的元素th:include和th:insert类似, 但是它仅仅是把fragment代码片段的文本内容插入进来.

    看下面的例子,比较不同:

    首先有一个名为copy的fragment

    <footer th:fragment="copy"> © 2011 The Good Thymes Virtual Grocery </footer>

    然后引用:

    <body> ... <div th:insert="footer :: copy"></div> <div th:replace="footer :: copy"></div> <div th:include="footer :: copy"></div> </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>

    6.2 参数化fragment签名

    为了创建函数化的fragment,可以给th:fragment添加一系列参数:

    <div th:fragment="frag (onevar,twovar)"> <p th:text="${onevar} + ' - ' + ${twovar}">...</p> </div>

    使用的时候可以这样:

    <div th:replace="::frag (${value1},${value2})">...</div> <div th:replace="::frag (onevar=${value1},twovar=${value2})">...</div>

    注意,按照下面的写法,参数顺序不重要,同样和上面的写法等效:

    <div th:replace="::frag (twovar=${value2},onevar=${value1})">...</div>

    6.2.1 fragment本地变量写法

    即使是没有定义参数,也可以网frag里传入变量。如:

    <div th:fragment="frag"> ... </div>

    可以用第二种语法(只能用第二种语法)来传参:

    <div th:replace="::frag (onevar=${value1},twovar=${value2})">

    这样即使frag后面没有参数签名,frag内部同样可以使用onevar和twovar这两个变量。这种写法和下面的写法是等效的:

    <div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">

    6.2.2 th:assert断言

    使用th:assert对表达式集合进行断言,表达式集合用逗号隔开。每个表达式的结果必须为真,否则会抛出异常:

    <div th:assert="${onevar},(${twovar} != 43)">...</div>

    这对于在片段签名中验证参数非常方便:

    <header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>

    6.3 灵活布局:不仅仅是fragment插入

    由于强大的fragment表达式的支持,我们可以把数字、bean对象等等设置为fragment的参数,即使不写参数签名都可以。

    这大大增强了模板的灵活性。举个例子,有这样一个fragment:

    <head th:fragment="common_header(title,links)"> <title th:replace="${title}">The awesome application</title> <!-- Common styles and scripts --> <link rel="stylesheet" type="text/css" media="all" th:href="@{/css/awesomeapp.css}"> <link rel="shortcut icon" th:href="@{/images/favicon.ico}"> <script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script> <!--/* Per-page placeholder for additional links */--> <th:block th:replace="${links}" /> </head>

    我们在另一个模板里通过th:replace来使用common_header模板:

    ... <head th:replace="base :: common_header(~{::title},~{::link})"> <title>Awesome - Main</title> <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"> <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}"> </head> ...

    上面common_header(~{::title},~{::link})中,~{::title}表达式选择了当前模板中的<title>元素,~{::link}表达式选中了当前模板中的所有<link>元素,最终结果是:

    ... <head> <title>Awesome - Main</title> <!-- Common styles and scripts --> <link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css"> <link rel="shortcut icon" href="/awe/images/favicon.ico"> <script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script> <link rel="stylesheet" href="/awe/css/bootstrap.min.css"> <link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css"> </head> ...

    6.3.1 使用空的fragment

    ~{}表示空的fragment。比如使用下面的模板:

    <head th:replace="base :: common_header(~{::title},~{})"> <title>Awesome - Main</title> </head> ...

    其最终结果如下:

    ... <head> <title>Awesome - Main</title> <!-- Common styles and scripts --> <link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css"> <link rel="shortcut icon" href="/awe/images/favicon.ico"> <script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script> </head> ...

    6.3.2 使用no-operation操作符

    使用{_}让模板使用默认的代码。如下所示模板一:

    ... <head th:replace="base :: common_header(_,~{::link})"> <title>Awesome - Main</title> <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"> <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}"> </head> ...

    common_header(_,~{::link})里传入了no-operation操作符。然后看一下common_header模板里的title部分:

    <title th:replace="${title}">The awesome application</title>

    那么最终的结果是模板一全部被common_header模板替换,而common_header模板的title元素被保留,没有被替换为Awesome - Main。最终结果如下:

    ... <head> <title>The awesome application</title> <!-- Common styles and scripts --> <link rel="stylesheet" type="text/css" media="all" href="/awe/css/awesomeapp.css"> <link rel="shortcut icon" href="/awe/images/favicon.ico"> <script type="text/javascript" src="/awe/sh/scripts/codebase.js"></script> <link rel="stylesheet" href="/awe/css/bootstrap.min.css"> <link rel="stylesheet" href="/awe/themes/smoothness/jquery-ui.css"> </head> ...

    6.3.3 fragment的更高级的条件断言

    考虑这样的条件,当用户是管理员的时候我们插入adminhead模板,否则就插入空的内容。那么我们可以这样做:

    <div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>

    同样地,如果用户是管理员我们就插入adminhead模板,否则什么都不做:

    ... <div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : _"> Welcome [[${user.name}]], click <a th:href="@{/support}">here</a> for help-desk support. </div> ...

    我们还可以判断fragment存不存在:

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

    6.4 移除代码

    被th:remove=“*”标记的元素会在Thymeleaf引擎渲染后移除。*的内容如下:

    all: Remove both the containing tag and all its children.body: Do not remove the containing tag, but remove all its children.tag: Remove the containing tag, but do not remove its children.all-but-first: Remove all children of the containing tag except the first one.none : Do nothing. This value is useful for dynamic evaluation. th:remove也可以使用条件判断: ```xml Link text not to be removed ```

    th:remove把null当做none的同义词:

    <a href="/something" th:remove="${condition}? tag">Link text not to be removed</a>

    "${condition}? tag"中,条件如果为假,返回值是null,所以上面的例子是合法的。

    6.5 布局继承

    我们可以写一个名为layoutFile的模板,声明一个名为layout的fragment,然后把这个layoutFile当做页面的原型。

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

    然后使用下面的替换来继承原型的内容(有点像编程语言继承的味道):

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

    最终的结果是,上面的模板的整个html元素都被原型替代,只不过,原型的title和content则会被上面的<title>Page Title</title>和<section>元素的内容替换。


    第七部分 本地变量和属性优先级

    7 本地变量

    7.1 定义本地变量

    Thymeleaf允许定义本地变量,本地变量的作用域被限制在申明变量的标签内部。先看一个例子:

    <tr th:each="prod : ${prods}"> ... </tr>

    上面的prod就是本地变量,它的作用域如下:

    在<tr>标签上,只要是优先级低于th:each的th:* 属性都可以使用prod变量所有<tr>标签的子元素都可以使用prod变量。 由于涉及到th:*属性的优先级问题,所以在申明本地变量的标签上使用时要小心了。

    除了使用th:each,最通用的申明本地变量的属性是th:with:

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

    th:each属性支持在申明变量时使用已经定义的变量:

    <div th:with="company=${user.company + ' Co.'},account=${accounts[company]}">...</div> 注意company变量的复用

    7.2 使用本地变量

    <p> Today is: <span th:with="df=#{date.format}" th:text="${#calendars.format(today,df)}">13 February 2011</span> </p>

    上面的例子中定义了本地变量df,在th:text属性中使用了它。注意:由于th:with属性的优先级高于th:text属性,所以可以在同一个<span>里使用。

    属性优先级

    OrderFeatureAttributes1Fragment inclusionth:insert th:replace2Fragment iterationth:each3Conditional evaluationth:if th:unless th:switch th:case4Local variable definitionth:object th:with5General attribute modificationth:attr th:attrprepend th:attrappend6Specific attribute modificationth:value th:href th:src …7Text (tag body modification)th:text th:utext8Fragment specificationth:fragment9Fragment removalth:remove

    第八部分 注释和Blocks

    8 Comments and Blocks

    8.1 标准的HTML/XML注释

    标准的HTML/XML注释是<!-- … -->

    <!-- User info follows 这是注释 --> <div th:text="${...}"> ... </div>

    8.2 Thymeleaf解析级别(parser-level )的注释

    parser-level级别的注释,只能被Thymeleaf在解析模板的时候移除。它是由<!–/ and /–>包裹起来的,Thymeleaf会把所有内容包括<!–/ and /–>移除掉:

    <!--/* This code will be removed at Thymeleaf parsing time! */-->

    既然被包裹的内容会被移除,那为什么还要用这个注释呢?

    考虑这样的场景,当我们仅仅只是打开一个静态的HTML页面,不经过Thymeleaf渲染,为了保持设计内容正常显示我们可以这样使用parser-level注释:

    <!--/*--> <div> you can see me only before Thymeleaf processes me! </div> <!--*/-->

    上面的代码在没有经过Thymeleaf渲染的情况下,依然能够正常显示。

    所以举个例子的话,我们可这样使用:

    <table> <tr th:each="x : ${xs}"> ... </tr> <!--/*--> <tr> ... </tr> <tr> ... </tr> <!--*/--> </table>

    8.3 原型注释prototype-only comment

    原型注释:只在原型里是注释,被解析后就不是注释。

    prototype-only类型的注释使用<!–// and //–> 标签包裹,在不使用Thymeleaf渲染的情况下直接打开HTML,注释内容依然是注释,而使用Thymeleaf渲染后,注释的内容得到保留,注释标签被移除,就像下面的例子。

    静态页面:

    <span>hello!</span> <!--/*/ <div th:text="${...}"> ... </div> /*/--> <span>goodbye!</span>

    经过Thymeleaf解析后的页面:

    <span>hello!</span> <div th:text="${...}"> ... </div> <span>goodbye!</span>

    这种诠释的设计应该是考虑到,在页面原型设计的时候,原型使用了一些HTML不支持的属性,所以在前后端分离的时候,为了不影响原型页面的展现将这些HTML不兼容的内容用原型级注释标签注释。

    8.4 th:block标签

    th:block是Thymeleaf里唯一的元素级处理器。它只是一个属性的容器,允许程序员在th:block标签上定义任何属性。经过Thymeleaf渲染后,th:block标签会被拿掉,但它的内容不会。

    看下面的例子:

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

    经过渲染后th:block标签会被去掉,去掉后整个模板就是HTML合法的了。但是不经过渲染,那么模板就不是HTML合法了。所以为了使模板合法,我们可以这样使用:

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

    第八部分 代码嵌入

    7 代码嵌入

    7.1 内联表达式

    使用内联表达式[[…]] or [(…)]往文本内容中嵌入表达式,它们的功能分别和th:text or th:utext 一样,可以往内联表达式内部使用任何可以在th:text 和 th:utext里使用的属性。

    [[…]]:和th:text一致,它对文本内容进行转义

    [(…)]:和th:utext一致,不对文本内容进行转义

    来比较以下它们的不同:

    如果msg='This is <b>great!</b>' <p>The message is "[(${msg})]"</p> 的结果是: <p>The message is "This is <b>great!</b>"</p> <p>The message is "[[${msg}]]"</p> 的结果是: <p>The message is "This is <b>great!</b>"</p>

    7.1.1 内联表达式 VS 普通模板

    内联表达式,如[(…)]虽然它的代码量比使用th:utext这种普通模板少,而且它们实现同样的功能,但是从网页原型设计的角度考虑,使用普通模板比使用内联表达式更能清晰地、高还原地表达原型设计。

    使用普通模板属性,即使是不经过Thymeleaf渲染,它依然能准确地显示原型内容。但是使用内联表达式,你看到的将会是这样的:

    Hello, [[${session.user.name}]]!

    所以,你可以看到这两者的区别了吧。

    7.1.3 禁用内联表达式

    使用th:inline="none"禁止Thymeleaf解析内联表达式:

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

    7.2 Text inlining

    Text inlining模式需要显式申明th:inline=“text”。其内容下一节讨论

    7.3 JavaScript inlining

    JavaScript内联允许在HTML模板模式中集成JavaScript脚本块。

    和text内联一样,JavaScript内联其实就是在JavaScript模板模式下对脚本内容进行处理(针对JavaScript语法进行表达式解析),因此下一节即将讲到的textual template modes的功能对于JavaScript inlining也是可用的。然而这一节我们先来看看怎么在JavaScript代码块中使用表达式。

    首先显式申明th:inline=“javascript”:

    <script th:inline="javascript"> ... var username = [[${session.user.name}]]; ... </script>

    经过解析后的代码:

    <script th:inline="javascript"> ... var username = "Sebastian \"Fruity\" Applejuice"; ... </script>

    从上面的代码可以发现:

    第一,JavaScript内联不仅解析了需要的文本内容,还使用分号对代码进行了正确的转义。第二,之所以会进行转义,是因为我们使用了[[...]]表达式,而如果使用[(...)]表达式,那么结果将如下: ```xml ```

    Thymeleaf会把这个注释后面的,分号前面的内容删除,也就是把"Gertrud Kiwifruit"删除,最后解析结果如下:

    <script th:inline="javascript"> ... var username = "Sebastian \"Fruity\" Applejuice"; ... </script>

    这样做的好处是,即使不经过Thymeleaf解析,原始的JavaScript代码模板也是合法的。

    7.3.1 高级内联取值和JavaScript序列化

    JavaScript的表达式求值并不仅仅局限在String类型上,其他类型的对象也能正确地转换,Thymeleaf支持的对象类型如下:

    StringsNumbersBooleansArraysCollectionsMapsBeans (objects with getter and setter methods)

    举个例子,如果使用下面的代码:

    <script th:inline="javascript"> ... var user = /*[[${session.user}]]*/ null; ... </script>

    ${session.user}表达式会正确地对User对象进行求值,然后转换为JavaScript语法格式的内容:

    <script th:inline="javascript"> ... var user = {"age":null,"firstName":"John","lastName":"Apricot", "name":"John Apricot","nationality":"Antarctica"}; ... </script>

    JavaScript模板模式下表达式的这种序列化能力由org.thymeleaf.standard.serializer.IStandardJavaScriptSerializer接口的实现类实现,可以通过模板引擎的StandardDialect对象的属性来进行配置。

    默认的JS序列化使用了Jackson library。但是如果classpath路径里没有Jackson,那么会使用内置的序列化机制来进行转换。

    7.4 CSS inlining

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

    可以看到,[[${classname}]]针对CSS进行了转义,把classname = 'main elems’转换成了main\ elems

    7.4.1 CSS natural templates

    通过注释来使用内联表达式,就像之前JavaScript中的内联表达式一样:

    <style th:inline="css"> .main\ elems { text-align: /*[[${align}]]*/ left; } </style>

    第九部分 文本模板的模式

    8 文本模板的模式

    8.1 文本型语法

    在Thymeleaf中支持文本型语法的三种模型是:TEXT, JAVASCRIPT和 CSS。同时还有两种与它们不同的模板,那就是支持标签语法的HTML 和 XML。

    文本型语法和标签型语法的不同之处是,文本型语法中没有标签的支持来表述模板的逻辑,所以文本型语法和标签型语法依赖的解析机制是不同的。

    文本型语法最基础的表述方法就是内联。内联在之前的章节其实已经讲到了,而且它非常适合直接在模板中插入文本,比如一个email的模板:

    Dear [(${name})], Please find attached the results of the report you requested with name "[(${report.name})]". Sincerely, The Reporter.

    但是为了实现更多更复杂的逻辑,我们需要一个非标签语法的支持:

    [# th:each="item : ${items}"] - [(${item})] [/]

    其实上面的模板是下面模板的简化版本:

    [#th:block th:each="item : ${items}"] - [#th:block th:utext="${item}" /] [/th:block]

    可以注意到我们使用了[#element …] 这样的语法来代表一个元素。这个元素和XML标签语法很像,以 [#element …]表示元素的开始,以[/element]来表示元素的结束,并且独立元素还可以最简化为[#element … /]的形式,只用一个/来表示结束,和XML中的内联元素很像。

    标准的唯一可以用来包裹element的是th:block,虽然我们可以通过自定义来实现自己的新element,但是Thymeleaf默认就支持th:block元素,所以不管有没有自定义的元素,th:block元素([#th:block …] … [/th:block])都可以简写为如下的形式:

    [# th:each="item : ${items}"] - [# th:utext="${item}" /] [/]

    观察上面的代码,[# th:utext="${item}" /]元素里只包含了一个内联的非转义属性th:utext,所以我们的代码可以进一步简化为:

    [# th:each="item : ${items}"] - [(${item})] [/]

    注意文本型语法需要让每个元素必须有引用属性,而且元素闭合。所以和HTML语法相比,这种语法和XML更像。

    接下来看一下JAVASCRIPT模板模式。注意,下面的代码是一个.js文件里的模板,而不是<script>代码块中的模板:

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

    经过Thymeleaf处理,最终模板如下:

    var greeter = function() { var username = "Bertrand \"Crunchy\" Pear"; alert("Hello" + " " + username); alert("Ol\u00E1" + " " + username); alert("Hola" + " " + username); };

    8.1.1 转义的元素属性

    为了避免与处在不同模式下的模板产生交互性影响(比如:在HTML模板里使用text内联语法,如果内联的表达式里使用了<号,那么它会对HTML的结构产生影响),所以Thymeleaf 3.0 支持元素中的属性使用转义后的形式。

    TEXT 模板模式里的属性必须是HTML转义的.JAVASCRIPT 模板模式里属性必须是 JavaScript-转义的.CSS 模板模式里的属性必须是CSS转义的.

    举个例子,下面是TEXT模式下的代码:

    [# th:if="${120<user.age}"] Congratulations! [/]

    注意到,代码中我们使用了<的转义符<,虽然转义后的模板看起来不太顺眼,但是如果在设计页面原型的时候使用了这样的代码,那么即使在不用Thymeleaf支持的情况下直接打开HTML文件,浏览器也不会吧user.age误认为一个标签元素。

    8.2 可扩展性

    文本型语法同标签语法一样,同样支持扩展。开发者可以自定义自己的元素和属性,使用时可以套用下面的语法模板:

    [#myorg:dosomething myorg:importantattr="211"]some text[/myorg:dosomething]

    8.3 在注释中插入代码

    JAVASCRIPT和CSS模板模式支持在注释语法/[+…+]/ 里插入可被解析的代码(TEXT模式下不支持)。Thymeleaf会在处理模板的时候解除对代码的注释,使代码可用。比如下面的代码:

    var x = 23; /*[+ var msg = "Hello, " + [[${session.user.name}]]; +]*/ var f = function() { ...

    经过处理后:

    var x = 23; var msg = "Hello, " + [[${session.user.name}]]; var f = function() { ...

    8.4 parser-level注释块:移除代码

    parser-level其意思就是说,代码内容在Thymeleaf解析的时候才会被当做是注释,然后删除它。这样的注释代码需要被包裹在/[- / 和 / -]/符号内:

    var x = 23; /*[- */ var msg = "This is shown only when executed statically!"; /* -]*/ var f = function() { ...

    上面的msg只有在不使用Thymeleaf的情况下才有用。

    在TEXT模式下:

    ... /*[- Note the user is obtained from the session, which must exist -]*/ Welcome [(${session.user.name})]! ...

    8.5 Natural JavaScript and CSS templates

    可被解析的注释可以用来构造JavaScript和CSS友好型的代码,比如之前讲到的内联型语法:

    ... var username = /*[[${session.user.name}]]*/ "Sebastian Lychee"; ...

    解析后的结果是:

    ... var username = "John Apricot"; ...

    同样地,我们可以把这种技巧使用到整个文本型语法当中:

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

    上面的代码是完全符合JavaScript语法的,当经过Thymeleaf的解析后,如果用户是admin,那么和下面的代码是等价的:

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

    然而你可以发现,/*[# th:if="${user.admin}"]*/代码后面的,分号前面的部分——alert(‘Welcome admin’)并没有被移除,原因是这种删除功能只有内联表达式有。

    Processed: 0.009, SQL: 9