熟练使用thymeleaf

来源:互联网 发布:淘宝如何找代理商 编辑:程序博客网 时间:2024/05/17 00:17

Thymeleaf是Web和独立环境的现代服务器端Java模板引擎,能够处理HTML,XML,JavaScript,CSS甚至纯文本。Thymeleaf的主要目标是提供一种优雅和高度可维护的创建模板的方式。

使用thymeleaf的第一步:
<html xmlns:th="http://www.thymeleaf.org">
在SpringMVC中我们一般将用来在前端页面上显示的数据都放在Model中

使用th:文本和外部化文本

外部化文本是从模板文件中提取模板代码的片段,以便它们可以保存在单独的文件(通常为.properties文件)中,文本的外部化片段通常称为“消息”。
消息总是有一个标识它们的键,而Thymeleaf允许您指定一个文本应该与具有以下#{…}语法的特定消息对应:
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>

标准表达式语法

上面已经看到了th:text,用这种语法表示:消息和变量表达式
但是,有更多类型的表达方式和更多有趣的细节来了解我们已经知道的内容。首先,我们来看看标准表达式功能的快速总结:

  • 简单表达:
    • 可变表达式: ${…}
    • 选择变量表达式: *{…}
    • 消息表达式: #{…}
    • 链接网址表达式: @{…}
    • 片段表达式: ~{…}
  • 文字操作:
    • 字符串连接: +
    • 文字替代: |The name is ${name}|
  • 布尔运算:
    • 二元运算符:and,or
    • 布尔否定(一元运算符): !,not
  • 条件运算符:
    • IF-THEN: (if) ? (then)
    • IF-THEN-ELSE: (if) ? (then) : (else)
    • 默认: (value) ?: (defaultvalue)

所有这些功能可以组合和嵌套:
'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

消息

我们已经知道,#{…}消息表达式允许我们链接外部文件,但是我们还没有想到的一个方面:如果消息文本不是完全静态的,会发生什么?例如,如果我们的应用知道谁是用户在任何时候访问该网站,我们是否想通过名字问候?
<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>

变量

我们已经提到,${…}表达式实际上是在上下文中包含的变量的Map上执行的OGNL对象。在Spring MVC启用的应用程序中,OGNL将被替换为SpringEL,但其语法与OGNL非常相似(实际上,在大多数常见情况下完全相同)。
<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>

实际上相当于:
((User) ctx.getVariable("session").get("user")).getName();

如果对象是map,则点和括号语法将等效于
执行对get(…)方法的调用。
${countriesByCode.ES}
${personsByName['Stephen Zucchini'].age}

对数组或集合的索引访问也用括号执行,
无引号写索引。
${personsArray[0].name}

方法可以被调用,甚至有参数。
${person.createCompleteNameWithSeparator('-')}

表达式基本对象

  • #ctx:上下文对象。
  • #vars: 上下文变量。
  • #locale:上下文区域设置。
  • #request:(仅在Web上下文中)HttpServletRequest对象。
  • #response:(仅在Web上下文中)HttpServletResponse对象。
  • #session:(仅在Web上下文中)HttpSession对象。
  • #servletContext:(仅在Web上下文中)ServletContext对象。
    比如: <span th:text="${#locale.country}">US</span>

表达式实用对象
除了这些基本的对象之外,Thymeleaf将为我们提供一组实用对象,它们将帮助我们在表达式中执行常见任务。

  • #dates:java.util.Date对象的方法:格式化,组件提取等
  • #calendars:类似#dates,但java.util.Calendar对象。
  • #numbers:用于格式化数字对象的方法。
  • #strings:String对象的方法:contains,startsWith,prepending / appending等
  • #objects:一般对象的方法。
  • #bools:布尔评估的方法。
  • #arrays:数组方法。
  • #lists:列表的方法
  • #sets:集合的方法。
  • #maps:地图方法

选择表达式(星号语法)
不仅可以将变量表达式写为${…},也可以写为*{…}。

有一个重要的区别:星号语法评估所选对象而不是整个上下文的表达式。也就是说,只要没有选定的对象,美元和星号语法就会完全相同。

什么是选定对象?使用该th:object属性的表达式的结果。比如:

<div th:object="${session.user}">    <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>    <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>    <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p></div>

链接URL

认识th:href属性:
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>
‘/gtvg/order/details?orderId=3’ (plus rewriting)
有些事情要注意:

  • th:href是一个修饰符属性:一旦处理,它将计算要使用的链接URL,并将该值设置为该标签的href属性<a>
  • 如果需要几个参数,这些参数将以逗号分隔: @{/order/process(execId=${execId},execType=’FAST’)}
  • URL路径中也允许使用变量模板: @{/order/{orderId}/details(orderId=${orderId})}
  • 以/开头的相对URL /order/details将以应用程序上下文名称自动为前缀。

与消息语法(#{…})的情况一样,URL基数也可以是评估另一个表达式的结果:
<a th:href="@{${url}(orderId=${o.id})}">view</a>
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>

服务器根相对URL
可以使用附加语法来创建服务器根相对(而不是上下文相对)URL,以链接到同一服务器中的不同上下文。这些URL将被指定为@{~/path/to/something}

无效文字

该null文本也可用于:
<div th:if="${variable.something} == null"> ...

条件表达式

条件表达式是根据评估条件(本身是另一个表达式)的结果来评估两个表达式中的一个。

让我们来看一个例子片段(引入另一个属性修改器,th:class):
<tr th:class="${row.even}? 'even' : 'odd'">
...
</tr>

条件表达式也可以使用括号嵌套:
<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">
...
</tr>

无操作令牌

无操作令牌由下划线符号(_)表示。

这个标记背后的想法是指定一个表达式的期望结果是什么也不做,即完全像可执行的属性(eg th:text)完全不存在。
<span th:text="${user.name} ?: _">no user authenticated</span>

设置属性值

设置任何属性的值

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

这个概念很简单:th:attr只需要一个赋值给一个属性的表达式。创建相应的控制器和消息文件后,处理此文件的结果将是:

<form action="/gtvg/subscribe">  <fieldset>    <input type="text" name="email" />    <input type="submit" value="¡Suscríbe!"/>  </fieldset></form>

除了新的属性值之外,你还可以看到,应用上下文名称已经自动前缀到URL基础/gtvg/subscribe,如前一章所述。

但是如果我们想要一次设置多个属性呢?XML规则不允许你在标记中设置属性两次,因此th:attr将以逗号分隔的分配列表,如:

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

将值设置为特定属性

但是th:attr几乎不用于模板。通常,你可以使用其他th:*属性,其任务是设置特定的标记属性(而不仅仅是任何属性th:attr)。

例如,要设置value属性,请使用th:value,还有th:action,th:href等等,这些属性有很多属性,每个都针对特定的HTML5属性。

附加和前缀

Thymeleaf还提供th:attrappend和th:attrprepend属性,其中追加(后缀)或预先准备(前缀)的评估,以现有的属性值的结果。

例如,您可能想要将要添加的CSS类的名称(未设置,刚添加)存储到上下文变量中的一个按钮中,因为要使用的特定CSS类将取决于用户所做的某些操作之前:
<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属性,其用于添加CSS类或片段风格的元素,但不覆盖现有的:
<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">
th:each属性,它是一个迭代属性,我们稍后再讨论一下

固定值布尔属性

HTML具有布尔属性的概念,没有值的属性,并且一个的假设意味着值为“true”。在XHTML中,这些属性只需要1个值,这是本身。

例如checked:

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

标准方言包括允许您通过评估条件设置这些属性的属性,以便如果评估为true,则该属性将被设置为其固定值,如果评估为false,则不会设置该属性:
<input type="checkbox" name="active" th:checked="${user.active}" />

迭代

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

保持迭代状态

使用时th:each,Thymeleaf提供了一种有助于跟踪你的迭代状态的机制:状态变量。
状态变量在th:each属性中定义并包含以下数据:

  • 当前的迭代索引,从0开始。这是index属性。
  • 当前的迭代索引,从1开始。这是count属性。
  • 迭代变量中元素的总量。这是size酒店。
  • 每次迭代的iter变量。这是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>

如果您没有显式设置状态变量,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: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>

这将创建一个链接到评论页面(带有URL /product/comments),其prodId参数设置为id产品,但仅当产品有任何评论时。
请注意,该th:if属性不仅将评估布尔条件。它的功能稍微超出了它,它将按照true以下规则评估指定的表达式:

  • 如果值不为空:
    • 如果值为布尔值,则为true。
    • 如果值是数字,并且不为零
    • 如果值是一个字符且不为零
    • 如果value是一个String,而不是“false”,“off”或“no”
    • 如果值不是布尔值,数字,字符或字符串。
  • (如果值为null,则th:如果将为false)。

此外,th:if还有一个逆属性,th:unless我们可以在前面的例子中使用它,而不是使用notOGNL表达式:
<a href="comments.html"
th:href="@{/comments(prodId=${prod.id})}"
th:unless="${#lists.isEmpty(prod.comments)}">view</a>

切换语句

还有一种方法可以有条件地使用Java 中的开关结构等价物:th:switch/ th:case来显示内容

<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属性被评估为true,th:case则相同切换上下文中的每个其他属性都将被评估为false。
默认选项指定为th:case=”*”

模板布局

包括模板片段

定义和引用片段
在我们的模板中,我们经常希望从其他模板中添加零件,如页脚,页眉,菜单等部分
为了做到这一点,Thymeleaf需要我们定义这些部分,“片段”,以便包含,可以使用th:fragment属性来完成。

假设我们要添加标准版权页脚,因此我们创建一个/WEB-INF/templates/footer.html包含此代码的文件:

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

上面的代码定义了一个片段叫做:copy,我们可以使用其中一个th:insert或多个th:replace属性轻松地包含在我们的主页中:

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

footer是”片段”的文件名,copy是文件中”th:fragment=’copy’”表达式中的值。
请注意,th:insert期望一个片段表达式(~{…}),它是一个导致片段的表达式。虽然在上面的例子中,这是一种不复杂的片段表达时,( ,~{)}包围完全是可选的,所以上面的代码将相当于:

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

片段规范语法
片段表达式的语法是非常简单的。有三种不同的格式:

  • “~{templatename::selector}”包含在命名的模板上应用指定的标记选择器导致的片段templatename。注意,selector可以只是一个片段名称,所以你可以~{templatename::fragmentname}像~{footer :: copy}上面那样指定一些简单的东西。
  • “~{templatename}”包括完整的模板命名templatename。
  • ~{::selector}”或”~{this::selector}”从相同的模板插入片段,进行匹配selector。如果在表达式出现的模板上找不到,则将模板调用堆栈(插入)遍历原始处理的模板(根),直到selector某个级别的匹配。
    双方templatename并selector在上面的例子可以是全功能的表达式(甚至条件语句!),如:
    <div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>

引用片段没有 th:fragment
由于Markup Selectors的功能,我们可以包括不使用任何th:fragment属性的片段。甚至可以使用不同于Thymeleaf知识的不同应用程序的标记代码:

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

我们可以使用上面的片段简单地引用它的id属性,类似于CSS选择器:

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

th:insert和th:replace之间的差异

  • th:insert 是最简单的:它将简单地插入指定的片段作为它的主机标签的主体。
  • th:replace实际上用指定的片段来替换它的主机标签。

所以这样一个HTML片段:

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

…在主机<div>标签中包含两次,如下所示:

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

…将导致:

<body>  ...  <div>    <footer>      &copy; 2011 The Good Thymes Virtual Grocery    </footer>  </div>  <footer>    &copy; 2011 The Good Thymes Virtual Grocery  </footer></body>

可参数化的片段签名

为了使模板片段创建一个更类似功能的机制,使用的片段th:fragment可以指定一组参数:

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

这需要使用这两种语法中的一种来从th:insert或者调用片段th:replace:

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

请注意,顺序在最后一个选项中不重要,但是前一个选项中是重要的

不带片段参数的片段局部变量
即使片段没有定义参数:

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

我们可以使用上面指定的第二个语法来调用它(只有第二个语法):
<div th:replace="::frag (onevar=${value1},twovar=${value2})">
这将相当于组合th:replace和th:with:
<div th:replace="::frag" th:with="onevar=${value1},twovar=${value2}">
先调用frag片段,再通过th:with传参数

灵活布局:不仅仅是片段插入

感谢片段表达式,我们可以为不是文本,数字,bean对象的片段指定参数,而是为标记片段指定参数。

这允许我们以一种方式创建我们的片段,使得它们可以丰富来自调用模板的标记,从而产生非常灵活的模板布局机制。

请注意在下面的片段中使用title和links变量:

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

我们现在可以这样调用:

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

……,结果会用实际<title><link>我们调用模板标签的值title和links变量,导致我们的片段插入时被定制:

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

使用无操作令牌
如果我们只想让我们的片段使用其当前标记作为默认值,那么”_”也可以用作片段的参数。再次,使用common_header示例:

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

这导致片段的这部分不被执行(title= no-operation):
<title th:replace="${title}">The awesome application</title>
所以结果是:

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

高级条件插入片段
emtpy片段和无操作令牌的可用性允许我们以非常简单和优雅的方式执行片段的条件插入。
例如,我们可以这样做,以便仅当用户是管理员时插入我们的common :: adminhead片段,否则插入空片段:
<div th:insert="${user.isAdmin()} ? ~{common :: adminhead} : ~{}">...</div>
另外,如果满足指定的条件,我们可以使用无操作令牌来插入片段,但是如果不满足条件,则不必修改保留标记:

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

删除模板片段(th:remove)

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

all属性中的值是什么意思?th:remove可以按照五种不同的方式表现:取决于它的价值:

  • all:删除包含标签及其所有子项。
  • body:不要删除包含的标签,但删除其所有的孩子。
  • tag:删除包含的标签,但不要删除其子项。
  • all-but-first:除去第一个包含标签的所有子项。
  • none: 没做什么。该值对于动态评估是有用的。

该th:remove属性可采取任何Thymeleaf标准表示,因为它返回允许字符串值中的一个,只要(all,tag,body,all-but-first或none)。
这意味着删除可能是有条件的,如:
<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将返回,因此不会执行删除。

合成th:block标签

标准方言中包含的Thymeleaf唯一的元素处理器(不是属性)是th:block。

th:block只是一个属性容器,允许模板开发人员指定他们想要的任何属性。Thymeleaf将执行这些属性,然后简单地制作块,而不是其内容消失。

因此,例如,当<tr>为每个元素创建需要多于一个的迭代表时,这可能是有用的:

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

内联

表达式

虽然标准方言使我们能够使用标签属性来做几乎所有的事情,但是有些情况下我们可以直接将表达式直接写入我们的HTML文本。例如,我们可以喜欢写这个:
<p>Hello, [[${session.user.name}]]!</p>
…而不是:
<p>Hello, <span th:text="${session.user.name}">Sebastian</span>!</p>
表达式[[…]]被认为是内切表达式在Thymeleaf中,并且在它们内部,我们可以使用任何一种在一个th:text属性中也是有效的表达式。

JavaScript内联

JavaScript内联允许

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

高级内联评估和JavaScript序列化

关于JavaScript内联的一个重要的事情是,这种表达式评估是智能的,不限于字符串。Thymeleaf将正确地使用JavaScript语法编写以下类型的对象:

  • 字符串
  • 数字
  • 布尔值
  • 数组
  • 集合
  • Map
  • Bean

例如,如果我们有以下代码:

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

该${session.user}表达式将评估为一个User对象,而Thymeleaf将正确地将其转换为JavaScript语法:

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

CSS内联

Thymeleaf还允许在CSS <style>标签中使用内联,例如:

<style th:inline="css">  ...</style>

例如,假设我们有两个变量设置为两个不同的String值:
classname = ‘main elems’
align = ‘center’

<style th:inline="css">    .[[${classname}]] {      text-align: [[${align}]];    }</style>

结果将是:

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

表达式实用程序对象

日期

  • #dates:java.util.Date对象的实用程序方法
    ${#dates.format(date)}    ${#dates.arrayFormat(datesArray)}    ${#dates.listFormat(datesList)}    ${#dates.setFormat(datesSet)}    ${#dates.formatISO(date)}    ${#dates.arrayFormatISO(datesArray)}    ${#dates.listFormatISO(datesList)}    ${#dates.setFormatISO(datesSet)}    ${#dates.format(date, 'dd/MMM/yyyy HH:mm')}    ${#dates.arrayFormat(datesArray, 'dd/MMM/yyyy HH:mm')}    ${#dates.listFormat(datesList, 'dd/MMM/yyyy HH:mm')}    ${#dates.setFormat(datesSet, 'dd/MMM/yyyy HH:mm')}

日历

  • calendars:类似#dates,但java.util.Calendar对象:
    ${#calendars.format(cal, 'dd/MMM/yyyy HH:mm')}    ${#calendars.arrayFormat(calArray, 'dd/MMM/yyyy HH:mm')}    ${#calendars.listFormat(calList, 'dd/MMM/yyyy HH:mm')}    ${#calendars.setFormat(calSet, 'dd/MMM/yyyy HH:mm')}

数字

  • #numbers:数字对象的实用程序方法:
/*  * 设置最小整数位 * Also works with arrays, lists or sets */${#numbers.formatInteger(num,3)}${#numbers.arrayFormatInteger(numArray,3)}${#numbers.listFormatInteger(numList,3)}${#numbers.setFormatInteger(numSet,3)}/*设置最小整数位数和千位分隔符: *'点'(POINT),'逗号'(COMMA),'空白'(WHITESPACE), */${#numbers.formatInteger(num,3,'POINT')}${#numbers.arrayFormatInteger(numArray,3,'POINT')}${#numbers.listFormatInteger(numList,3,'POINT')}${#numbers.setFormatInteger(numSet,3,'POINT')}//以上是格式化Integer类型的数字/* *设置最小整数位数和小数位数(精确) */${#numbers.formatDecimal(num,3,2)}${#numbers.arrayFormatDecimal(numArray,3,2)}${#numbers.listFormatDecimal(numList,3,2)}${#numbers.setFormatDecimal(numSet,3,2)}/* *设置最小整数位数和(精确)小数位数,以及小数分隔符 */${#numbers.formatDecimal(num,3,2,'COMMA')}${#numbers.arrayFormatDecimal(numArray,3,2,'COMMA')}${#numbers.listFormatDecimal(numList,3,2,'COMMA')}${#numbers.setFormatDecimal(numSet,3,2,'COMMA')}/* *设置最小整数位数和(精确)小数位数,以及千位和小数点分隔符。 */${#numbers.formatDecimal(num,3,'POINT',2,'COMMA')}${#numbers.arrayFormatDecimal(numArray,3,'POINT',2,'COMMA')}${#numbers.listFormatDecimal(numList,3,'POINT',2,'COMMA')}${#numbers.setFormatDecimal(numSet,3,'POINT',2,'COMMA')}