Thymeleaf教程 (八) 模板布局(thymeleaf的主要技术优势)

来源:互联网 发布:口腔医学网络教育 编辑:程序博客网 时间:2024/05/16 05:45

这节主要介绍模板的引入。及如何在不改变前端人员的html显示结果的情况下设计模板(通过属性配置动态时不显示的部分)。

模板模块导入

首先定义一个/WEBINF/templates/footer.html文件:

<!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; 2011 The Good Thymes Virtual Grocery        </div>    </body></html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上面的代码定义了一个片段称为copy,我们可以很容易地使用th:include 或者 th:replace属性包含在我们的主页上:

<body>...<div th:include="footer :: copy"></div></body>
  • 1
  • 2
  • 3
  • 4

include的表达式想当简洁。这里有三种写法:

  • “templatename::domselector” 或者 “templatename::[domselector]”引入模板页面中的某个模块。
  • “templatename”引入模板页面。
  • “::domselector” 或者 “this::domselector” 引入自身模板的模块 
    上面所有的templatename和domselector的写法都支持表达式写法:
<div th:include="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>
  • 1

不使用th:fragment来引用模块

...<div id="copy-section">&copy; 2011 The Good Thymes Virtual Grocery</div>...
  • 1
  • 2
  • 3
  • 4
  • 5

我们可以用css的选择器写法来引入

<body>...<div th:include="footer :: #copy-section"></div></body>
  • 1
  • 2
  • 3
  • 4

th:include 和 th:replace的区别

th:include和th:replace都可以引入模块,两者的区别在于 
th:include:引入子模块的children,依然保留父模块的tag。 
th:replace:引入子模块的所有,不保留父模块的tag。 
举个栗子:

<footer th:fragment="copy">&copy; 2011 The Good Thymes Virtual Grocery</footer>
  • 1
  • 2
  • 3

引入界面:

<body>...<div th:include="footer :: copy"></div><div th:replace="footer :: copy"></div></body>
  • 1
  • 2
  • 3
  • 4
  • 5

结果是:

<body>...<div>&copy; 2011 The Good Thymes Virtual Grocery</div><footer>&copy; 2011 The Good Thymes Virtual Grocery</footer></body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

给引入模块添加参数

我们的模块当中肯定有需要有参数的需求:

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

比如在文本中显示参数可以这样中:

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

第二种用法中参数顺序并不重要:

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

引入没有被定义的模块参数

这段模块没有定义参数

<div th:fragment="frag">...</div>
  • 1
  • 2
  • 3

我们可以并且只能用第二种方式引入:

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

这个也等同于:

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

解析式删除不需要的内容(这才是此技术最吸引人的地方,可以让前端和后端使用同一个模板,并且都能看到自己想要的效果)

一般情况下后端处理后的界面是这样的:

<table>    <tr>        <th>NAME</th>        <th>PRICE</th>        <th>IN STOCK</th>        <th>COMMENTS</th>    </tr>    <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:unless="${#lists.isEmpty(prod.comments)}">view</a>        </td>    </tr></table>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这只是个模板文件,不是前端写好的预览文件,那么要和前端写好的预览文件一至,我们一般情况下只能增加虚拟的行.

<table>    <tr>        <th>NAME</th>        <th>PRICE</th>        <th>IN STOCK</th>        <th>COMMENTS</th>    </tr>    <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:unless="${#lists.isEmpty(prod.comments)}">view</a>        </td>    </tr>    <tr class="odd">        <td>Blue Lettuce</td>        <td>9.55</td>        <td>no</td>        <td>        <span>0</span> comment/s    </td>    </tr>    <tr>        <td>Mild Cinnamon</td>        <td>1.99</td>        <td>yes</td>        <td>        <span>3</span> comment/s        <a href="comments.html">view</a>        </td>    </tr></table
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

OK.现在我们有三行了。看起来和前端的预览文件一致了。那么我们通过thymeleaf处理后的结果肯定是正确的内容+虚拟的内容,其实我们要的只是正确的内容而已。 
为了解决这个问题th:remove华丽登场了。

<table>    <tr>        <th>NAME</th>        <th>PRICE</th>        <th>IN STOCK</th>        <th>COMMENTS</th>    </tr>    <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:unless="${#lists.isEmpty(prod.comments)}">view</a>        </td>    </tr>    <tr class="odd" th:remove="all">        <td>Blue Lettuce</td>        <td>9.55</td>        <td>no</td>        <td>        <span>0</span> comment/s        </td>    </tr>    <tr th:remove="all">        <td>Mild Cinnamon</td>        <td>1.99</td>        <td>yes</td>        <td>        <span>3</span> comment/s        <a href="comments.html">view</a>        </td>    </tr></table>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

这个模板在后端开发通过thymeleaf解析后会移除掉有th:remove的标签,满足后端的预期。同时在前端眼中,也是自己预览的效果。

th:remove总共有五种属性:

  • all : 移除tag标记和children。
  • body:保留tag标记和移除children。
  • tag :移除tag和保留children.
  • all-but-first :保留tag和移除除了第一个外的所有children。
  • none :什么都不做。 
    以下是all-but-first的栗子:
<table>    <thead>        <tr>            <th>NAME</th>            <th>PRICE</th>            <th>IN STOCK</th>            <th>COMMENTS</th>        </tr>    </thead>    <tbody th:remove="all-but-first">        <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:unless="${#lists.isEmpty(prod.comments)}">view</a>            </td>        </tr>        <tr class="odd">            <td>Blue Lettuce</td>            <td>9.55</td>            <td>no</td>            <td>            <span>0</span> comment/s            </td>        </tr>        <tr>            <td>Mild Cinnamon</td>            <td>1.99</td>            <td>yes</td>            <td>            <span>3</span> comment/s            <a href="comments.html">view</a>            </td>        </tr>    </tbody></table>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

当然属性也支持表达式:

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