Thymeleaf 布局

布局

创建一个包含模板间共享布局的模板,通常这样的模板包含: 页面头部、导航栏、脚部、内容展示区域。

Layout.html

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
  <title>Layout page</title>
  <script src="common-script.js"></script>
</head>
<body>
  <header>
    <h1>My website</h1>
  </header>
  <section layout:fragment="content">
    <p>Page content goes here</p>
  </section>
  <footer>
    <p>My footer</p>
    <p layout:fragment="custom-footer">Custom footer here</p>
  </footer>  
</body>
</html>

注意事项:
1. html标签上附上命名空间
2. section与脚部p标签上使用layout:fragment属性

这些是布局中的插入候选槽点,通过匹配内容模板中片段进行替换。
创建一些内容模板:

Content1.html

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
  layout:decorate="~{Layout}">
<head>
  <title>Content page 1</title>
  <script src="content-script.js"></script>
</head>
<body>
  <section layout:fragment="content">
    <p>This is a paragraph from content page 1</p>
  </section>
  <footer>
    <p layout:fragment="custom-footer">This is some footer content from content page 1</p>
  </footer>
</body>
</html>

html标签中的layout:decorate说明哪一个布局模板使用这个内容模板进行装饰。内容模板定义自身标题与脚本、contentcustom-footer片段。custom-footer片段处于footer元素内部,这其实是不必要的,但是可能会是很方便的,如果想要做内容模板的静态模板,这是一开始使用Thymeleaf的原因之一。

在一个模板内片段名称必须唯一,否则可能会出现片段不匹配,各种各样的可笑事情会接踵而至。

不管如何,一旦告知Thymeleaf处理Content1.html,最终的页面会是这样子:

<!DOCTYPE html>
<html>
<head>
  <title>Content page 1</title>
  <script src="common-script.js"></script>
  <script src="content-script.js"></script>
</head>
<body>
  <header>
    <h1>My website</h1>
  </header>
  <section>
    <p>This is a paragraph from content page 1</p>
  </section>
  <footer>
    <p>My footer</p>
    <p>This is some footer content from content page 1</p>
  </footer>  
</body>
</html>

内容模板装饰Layout.html,结果是布局的组合,加上内容模板的片段(两个模板的<head>元素,来自内容模板的<title>元素替换布局文件内的,所有的元素来自布局文件,但是由所有指定的内容模板进行替换)

想了解更多可以如何控制<head>元素合并,参看<head>元素合并一小节。

装饰进程重定向处理从内容模板至布局,将layout:fragment部分从内容模板中挑选出来,因为布局需要它们。正因如此,任何在layout:fragment之外的东西实际从未得到执行,这说明在内容模板中不能这样做:

<div th:if="${user.admin}">
 <div layout:fragment="content">
   ...
 </div>
</div>

如果布局模板想要’内容’片段,那么会得到那个片段,不顾任何所在条件,因为那些条件不会执行。

如果说只想用绝对最小HTML代码量替换装饰器脚部:

Content2.html

<p layout:decorate="~{Layout}" layout:fragment="custom-footer">
  This is some footer text from content page 2.
</p>

这就是全部所需的东西!<p>标签同时用作根元素与片段定义,生成一个像这样的页面:

<!DOCTYPE html>
<html>
<head>
  <title>Layout page</title>
  <script src="common-script.js"></script>
</head>
<body>
  <header>
    <h1>My website</h1>
  </header>
  <section>
    <p>Page content goes here</p>
  </section>
  <footer>
    <p>My footer</p>
    <p>
      This is some footer text from content page 2.
    </p>
  </footer>  
</body>
</html>

可以把布局看作母版,会得以填充或者被内容(子模板)覆盖,仅当内容会填充/覆盖父类。以这种方式,布局充当某种’默认’,内容充当这种默认之上的实现。

给布局传送数据

子模板向上给母版布局传送数据,在涉及到布局/装饰过程的任意元素上使用th:with/ data-th-with属性处理器,可以在任何地方layout:decorate/ data-layout-decorate 或者可以发现 layout:fragment/data-layout-fragment, 例如:
孩子/内容模板:

<html layout:decorate="your-layout.html" th:with="greeting='Hello!'">

父类/布局模板:

<html>
  ...
  <p th:text="${greeting}"></p> <!-- You'll end up with "Hello!" in here -->

将来,或许会增加支持使用分片局部变量,很像Thymeleaf用于创建片段签名。

配置标题

鉴于布局方言自动用内容模板中所发现的重载布局<title>,可能会发现自己重复布局中发现的标题部分,尤其是想要创建面包屑或者在页面标题中保留页面名称。layout:title-pattern处理器可以免除重复布局标题的问题,通过使用一些特殊标记以想要标题如何出现的模式。
这是一个例子:

Layout.html

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
  <title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">My website</title>
</head>
...
</html>

layout:title-pattern处理器采取简单字符串,识别两种特殊标记:$LAYOUT_TITLE$CONTENT_TITLE。每种标记在结果页中会被各自相应的标题替换。所以,如果有下面的内容模板:

Content.html

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
  layout:decorator="Layout">
<head>
  <title>My blog</title>
</head>
...
</html>

结果页会是这样:

<!DOCTYPE html>
<html>
<head>
  <title>My website - My blog</title>
</head>
...
</html>

这对<title>元素内的静态/内联文本或者<title>元素上发现使用th:text的动态文本均有效。

上述例子中的模式在布局中指定,所以对所有使用布局的内容模板均适用。如果在内容模板中指定另一种标题模式,那么会覆盖布局中发现的那个,允许细粒度的控制标题的展现形式。

可重用模板

假如发现有一些HTML或者结构经常性地重复,想要做成自己的模板从不同地方插入以便减少代码重复。(模块化Thymeleaf?)这个的例子可能会是一个模态面板,由几个HTML元素与CSS类构成,在网页应用中产生一个新窗口的效果:

Modal.html

<!DOCTYPE html>
<html>
<body>
  <div id="modal-container" class="modal-container" style="display:none;">
    <section id="modal" class="modal">
      <header>
        <h1>My Modal</h1>
        <div id="close-modal" class="modal-close">
          <a href="#close">Close</a>
        </div>
      </header>
      <div id="modal-content" class="modal-content">
        <p>My modal content</p>
      </div>
    </section>
  </div>
</body>
</html>

会发现可以将一些东西转换成像头部、ID的变量,以便包含Modal.html的页面可以设定它们自己的名称/ID。继续尽可能泛型化编写模态代码,然而会遇到填充自己的模态框内容的问题,那是开始接触一些限制的地方。

一些页面使用单一消息的模态框,其他想要使用模态框容纳一些更复杂的东西比如接受用户输入的表单。模态框可能性变得无休无止,但是未支持想象,发现自己得不得将这段模态框代码拷贝到每一个模板中,每一次使用场合变化相应内容,重复同样的HTML代码维持同样的外观感受,打破了过程中的DRY原则。
主要妨碍适当重用的事情是无法将HTML元素传递至插入模板中。这正是layout:insert有用的地方。它运作起来完全像th:insert,但是通过指定与实现片段很像内容/布局实例,可以创建一个公共的结构,对插入它的模板使用场合作出响应。

这是一个更新的模态框模板,使用Thymeleaf与layout:fragment属性定义一个可替换的模态框内容部分以变得更加泛型化:

Modal2.html

Modal2.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
  xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<body layout:fragment="modal(modalId, modalHeader)">
  <div th:id="${modalId} + '-container'" class="modal-container" style="display:none;">
    <section th:id="${modalId}" class="modal">
      <header>
        <h1 th:text="${modalHeader}">My Modal</h1>
        <div th:id="'close-' + ${modalId}" class="modal-close">
          <a href="#close">Close</a>
        </div>
      </header>
      <div th:id="${modalId} + '-content'" class="modal-content">
        <div layout:fragment="modal-content">
          <p>My modal content</p>
        </div>
      </div>
    </section>
  </div>
</body>
</html>

现在可以插入这个模板,使用layout:insert处理器与无论怎样需要实现modal-content片段,通过在调用模板插入元素内创建同样名称的片段:

Content.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
  xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
  ...
  <div layout:insert="Modal2 :: modal(modalId='message', modalHeader='Message')" th:remove="tag">
    <p layout:fragment="modal-content">Message goes here!</p>
  </div>
  ...
</html>

就像内容/布局实例,插入模板layout:fragment会被匹配片段名称的元素替换掉。在这种场合下,Modal2.html的整个modal-content部分会被上述自定义段落替换掉。这是结果:

<!DOCTYPE html>
<html>
  ...
  <div id="message-container" class="modal-container" style="display:none;">
    <section id="message" class="modal">
      <header>
        <h1>Message</h1>
        <div id="close-message" class="modal-close">
          <a href="#close">Close</a>
        </div>
      </header>
      <div id="message-content" class="modal-content">
        <p>Message goes here!</p>
      </div>
    </section>
  </div>
  ...
</html>

定义在模板内包含Modal2.html的自定义消息作为模态框内容的一部分。在插入模板上下文环境中的片段与用于内容/布局过程中的片段一样工作:如果片段未在模板中定义,那么它不会覆盖插入模板中的内容,使得在可重用版本中创建默认。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值