5)Thymeleaf 模板布局 th:fragment、th:replace、th:insert、th:remove

x33g5p2x  于2021-12-24 转载在 其他  
字(7.9k)|赞(0)|评价(0)|浏览(320)

th:fragment  模板布局

模板片段说明

1、模板中,经常希望从其他模板中包含⼀些部分,如⻚眉,⻚脚,公共菜单等部分,为了做到这⼀点,Thymeleaf 可以使⽤th:fragment 属性来定义被包含的模版⽚段,以供其他模版包含。

如下所示定义模板片段:
divth:fragment="copy"
      © 2011 The Good Thymes Virtual Grocery

</div>

2、上⾯的代码定义了⼀个名为 copy 的⽚段,然后可以使⽤ th:insert 或 th:replace属性(Thymeleaf 3.0 不再推荐使⽤ th:include)轻易地包含进需要的页面中。

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

3、footer:表示模板名称,就是 html 文件的名称,如果是 springboot 开发,则根据 Spring Boot 配置的 Thymeleaf 映射查找。

copy:表示模板片段名称,即 th:fragment="copy" 的名称

th:insert 中的 〜{...} 表示⽚段表达式,它是可选的,上⾯的代码等价于如下所示写法,这也是实际开发中常用的写法:

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

⽚段表达式语法

1、th:insert/th:replace 中,〜{...} 片段表达式是可选的,可写可不写

2、被引用的 th:fragment 模板片段与引用的 th:insert 或 th:replace 可以是在同一个 Html 文件中,也可以不在同一个 html 中。

⽚段表达式语法⾮常简单,有如下三种不同的格式:
1)〜{templatename :: selector}-:- templatename 表示模板名称(html 文件名称),springboot 项目中就是 “templates”目录下的 html 文件名称,它根据 springboot 对 thymeleaf 的规则进行映射。selector 即可以是 th:fragment 定义的片段名称,也可以选择器,如标签的 id 值、CSS 选择器、或者 XPath 等。

2)〜{templatename}-:- 包含名为 templatename 的整个模板。
3)〜{:: selector} 或 〜{this :: selector}:包含在同⼀模板中的指定选择器的⽚段

模板名 templatename 和选择器 selector 都可以是表达式(甚⾄是条件表达式!)如:

<div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>

再次注意,th:insert/th:replace 中的 〜{...} 是可选的。

〜{templatename :: selector}中的 selector 可以是普通的选择器,如标签的 id 值、CSS 选择器、或者 XPath 等:
...

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

可以直接通过 ID 属性来引⽤上⾯的⽚段,也可以是 CSS 选择器(.content)、XPath (//div[@class='content']):

...

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

...

th:insert 与 th:replace 区别

1、Thymeleaf 3.0 之后不再推荐使⽤ th:include.
th:insert:将被引用的模板片段插⼊到自己的标签体中
th:replace:将被引用的模板片段替换掉自己
th:include:类似于 th:insert,⽽不是插⼊⽚段,它只插⼊此⽚段的内容。

<!--1、比如抽取的公用代码片段如下-->
<footer th:fragment="copy">
    &copy; 2011 The Good Thymes Virtual Grocery
</footer>
 
<!--2、采用如下三种方式进行引用-->
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
 
<!--3、则三种引用方式的效果分别对应如下-->
<div>
    <footer>
        &copy; 2011 The Good Thymes Virtual Grocery
    </footer>
</div>
 
<footer>
    &copy; 2011 The Good Thymes Virtual Grocery
</footer>
 
<div>
    &copy; 2011 The Good Thymes Virtual Grocery
</div>

再次强调:

1)th:fragment 与 th:insert、th:replace、th:include 可以在同一模板中,也可以是在不同的模板中

2)th:insert 、th:replace、th:include 在标签中进行引用时可以不用片段表达式 〜{...},但是行内写法时,必须要用片段表达式,如:~{}、[(~{})]

参数化片段签名

为了使模板⽚段具有类似函数的功能,th:fragment 定义的⽚段可以指定⼀组参数:

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

于是在 th:insert、th:replace 引用模板片段的时候,就可以传递参数过去:

<div th:replace="templatename :: frag (${value1},${value2})">...</div>     //按参数定义时的顺序进行传递 <div th:replace="templatename :: frag (onevar=${value1},twovar=${value2})">...</div>     //按参数名称传递,此时与参数定义的顺序无关

比如公共菜单中高亮显示当前菜单时就可以使用这种传递参数的方式进行标识当前活动的菜单。

编码示例

SpringBoot 项目结构如上:
1)commons/commons.html:公共页面,其中包含公共的头部,公共的底部,公共的左侧菜单。项目中这些公共部分都是得提取出来,然后供各个模块引用即可。公共的头、尾、左侧菜单可以放在一个 html 模板中,也可以放在单独的模板中。

2)tiger/home.html:tiger 的首页,会引用公共模板片段,自己的 css 文件为 css/tigerHome.css

3)user/home.html:用户的首页,会引用公共模板片段,自己的 css 文件为 css/userHome.css

4)css/commons.css 这是公共模板 commons.html 中的代码片段引用的样式,因为 tiger/home.html、user/home.html 会引用 commons.html 中的代码片段,这些片段又需要 commons.css 中的样式,所以 commons.css 必须也在 tiger/home.html、user/home.html 文件中进行 link 引用。

当点击:http://localhost/thymeleaf/tiger/home 时进入 tiger/home.html

当点击:http://localhost/thymeleaf/user/home 时进入 user/home.html

commons.html 公共模板

公共页面,其中包含公共的头部,公共的底部,公共的左侧菜单。项目中公共部分都可以提取出来,然后供各个模块引用即可。公共的头、尾、左侧菜单可以放在一个 html 模板中,也可以放在单独的模板中。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8">
    <title>公共模板</title>
    <!-- 公共模板中,这个样式文件可以不用引用,因为不会从浏览器直接访问公共模板,而是访问具体的模块-->
    <!-- 注意:如果模板片段中应用了其中的样式,则谁引用了模板片段,谁就要导入此样式文件-->
    <link type="text/css" rel="stylesheet" th:href="@{/css/commons.css}">
</head>
<body>
<!--定义公共的头部,commonHeader 为模板片段名称-->
<header th:fragment="commonHeader">
    <h2>护龙山庄</h2>
</header>

<article>
    <!-- 定义公共的左侧菜单,commonMenu 表示片段名称,index 为传递进来的参数-->
    <div class="articleLeft" th:fragment="commonMenu(index)">
        <!-- 根据传递的参数 index 来设置当前活动的菜单样式;三元运算符,第三个参数缺省,当 ? 判断为 false 时返回 null-->
        <div><a th:href="@{/user/home}" th:class="${index}==0?'activeMenu'">用户首页</a></div>
        <div><a th:href="@{/tiger/home}" th:class="${index}==1?'activeMenu'">Tiger Home</a></div>
        <!-- 同理如果还有其它更多的菜单时,可以依次往下传递参数-->
    </div>
    <div class="content">
    </div>
</article>

<!--定义公共的底部,这里使用 id 选择器,当然也可以用片段名,如 th:fragment="commonFooter"-->
<footer id="commonFooter">
    Copyright © 1998 - 2018 YYServer. All Rights Reserved
</footer>

</body>
</html>

user/home.html 引入模板片段 

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8">
    <title>用户首页</title>
    <!-- 被引用的模板片段应用了 commons.css 中的样式时,在引用的模块中必须导入进来-->
    <link type="text/css" rel="stylesheet" th:href="@{/css/commons.css}">
    <!-- 这是本文件自己的样式文件-->
    <link type="text/css" rel="stylesheet" th:href="@{/css/userHome.css}">
</head>
<body>
<!--引入公共的头部,使用 replace 替换自己的 header 标签-->
<!--注意:模板名称会根据 springboot 对 Thymeleaf 的配置规则进行映射-->
<!--默认情况下就是 templates 目录下的 html 文件-->
<header th:replace="commons/commons :: commonHeader"></header>
<article>
    <!-- 引入公共的左侧菜单,使用 replace 替换自己的 div;传递参数 0 ,与定义时约定的参数要保持一致-->
    <div th:replace="commons/commons :: commonMenu(0)">
    </div>
    <div class="content">
        <h2 class="hint">用户首页</h2>
    </div>
</article>
<!--引入公共的底部,这里使用 id 选择器,而不是片段名称-->
<footer th:replace="commons/commons :: #commonFooter"></footer>
</body>
</html>

与此同理,tiger/home.html 写法也是一样,只是在引入公共的左侧菜单时传递的参数不同:

<div th:replace="~{commons/commons :: commonMenu(1)}">

如果还有更多的公共左侧菜单,则只需要约定传递不同的参数即可。

运行效果

th:remove 删除模版片段

1、如果想在某些情况下直接删除模板中的某些代码片段,则可以使用 th:remove,如 <tr th:remove="all">...。

2、th:remove 可以有五种不同的删除⽅式:
1)all:删除包含标签及其所有⼦项
2)body:不删除包含标签,但删除所有的⼦项
3)tag:删除包含标签,但不要删除其⼦项
4)all-but-first:删除第一个子项以外的其它所有子项
5)none:什么都不做。 该值对于动态计算有⽤。null 也会被视为 none

<body>
<!--hint 样式会为 div 加一个 1px 的红色边框-->
<div class="hint">
    <p>th:remove<span> !</span></p>
</div>
<!--all:删除所有标签,包含自己-->
<div class="hint" th:remove="all">
    <p>all<span> @</span></p>
</div>
<!--body:不删除自己,但删除所有子项-->
<div class="hint" th:remove="body">
    <p>body<span> #</span></p>
</div>
<!--tag:删除自己,但不删除所有子项-->
<div class="hint" th:remove="tag">
    <p>tag<span> $</span></p>
</div>
<!--all-but-first:除第一个子项以外,删除其它所有子项-->
<div class="hint" th:remove="all-but-first">
    <p>all-but-first1<span> %</span></p>
    <!-- 上面第一个子项不会被删除,从第二个子项开始全部会被删除-->
    <p>all-but-first2<span> %</span></p>
</div>
<!--none:不做任何处理。该值对于动态计算时有用-->
<div class="hint" th:remove="none">
    <p>none<span> ^</span></p>
</div>
</body>

   只要 th:remove 返回⼀个允许的字符串值(all,tag,body,all-but-first ,none),则 th:remove 属性可以使⽤任何 Thymeleaf 标准表达式,如:
<a href="/something" th:remove="${condition}? tag : none">Link text not to be removed</a>

   th:remove 将 null 视为 none,因此以下⼯作与上述示例作用相同:

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

   在这种情况下,如果 $ {condition} 为 false,则返回 null,因此不会执⾏删除。

相关文章