欢迎您的访问
专注于分享最有价值的互联网技术干货

四、标准表达语法

几个T的资料等你来白嫖
双倍快乐
一定要收藏这个宝藏网站防止丢失,求助资源~!!!

四、标准表达语法

我们将在杂货店虚拟 Store 的开发中稍作 Rest,以了解 Thymeleaf Standard 方言最重要的部分之一:Thymeleaf Standard Expression 语法。

我们已经看到了用这种语法表示的两种有效属性值:消息和变量表达式:

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

<p>Today is: <span th:text="${today}">13 february 2011</span></p>

但是有更多类型的表达式,还有更多有趣的细节来了解我们已经知道的表达式。首先,让我们看一下标准表达式功能的快速摘要:

  • Simple expressions:
  • 变量表达式:${...}

    • 选择变量表达式:*{...}
    • 讯息表达:#{...}
    • 链接 URL 表达式:@{...}
    • 片段表达式:~{...}
  • Literals
  • LiteralsLiterals:'one text''Another one!',…

    • 数字 Literals:0343.012.3,…
    • 布尔 Literals:truefalse
    • 空 Literals:null
    • Literals 标记:onesometextmain,…
  • Text operations:
  • 字符串串联:+

    • Literals 替换:|The name is ${name}|
  • Arithmetic operations:
  • 二进制运算符:+-*/%

    • 减号(一元运算符):-
  • Boolean operations:
  • 二进制运算符:andor

    • 布尔取反(一元运算符):!not
  • 比较和equal:
  • 比较器:><>=<=(gtltgele)

    • 等于运算符:==!=(eqne)
  • Conditional operators:
  • 如果-则:(if) ? (then)

    • 如果-则-其他:(if) ? (then) : (else)
    • 默认值:(value) ?: (defaultvalue)
  • Special tokens:
  • 无操作:_

所有这些功能都可以组合和嵌套:

'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))

4.1 Messages

众所周知,#{...}消息表达式使我们可以链接此链接:

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

…to this:

home.welcome=¡Bienvenido a nuestra tienda de comestibles!

但是,我们仍然没有想到的一个方面:如果消息文本不是完全静态的,会发生什么?例如,如果我们的应用程序知道随时有谁在访问该站点,而我们想按名称向他们打招呼,该怎么办?

<p>¡Bienvenido a nuestra tienda de comestibles, John Apricot!</p>

这意味着我们需要在消息中添加一个参数。像这样:

home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!

根据java.text.MessageFormat标准语法指定参数,这意味着您可以格式化java.text.*包中类的 API 文档中指定的数字和日期。

为了为我们的参数指定一个值,并给定一个名为user的 HTTP 会话属性,我们可以:

<p th:utext="#{home.welcome(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
</p>

请注意,此处使用th:utext表示格式化的消息将不会被转义。本示例假定user.name已被转义。

可以指定几个参数,以逗号分隔。

消息密钥本身可以来自变量:

<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
</p>

4.2 Variables

我们已经提到过${...}表达式实际上是在上下文中包含的变量 Map 上执行的 OGNL(对象图导航语言)表达式。

有关 OGNL 语法和功能的详细信息,您应该阅读OGNL 语言指南
在启用 Spring MVC 的应用程序中,OGNL 将替换为 SpringEL ,但是其语法与 OGNL 的语法非常相似(实际上,对于大多数常见情况而言,它们是完全相同的)。

根据 OGNL 的语法,我们知道该表达式在:

<p>Today is: <span th:text="${today}">13 february 2011</span>.</p>

…实际上等于:

ctx.getVariable("today");

但是 OGNL 允许我们创建功能更强大的表达式,这就是这种方式:

<p th:utext="#{home.welcome(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
</p>

…通过执行以下操作获取用户名:

((User) ctx.getVariable("session").get("user")).getName();

但是,getter 方法导航只是 OGNL 的功能之一。让我们看看更多:

/*
 * Access to properties using the point (.). Equivalent to calling property getters.
 */
${person.father.name}

/*
 * Access to properties can also be made by using brackets ([]) and writing 
 * the name of the property as a variable or between single quotes.
 */
${person['father']['name']}

/*
 * If the object is a map, both dot and bracket syntax will be equivalent to 
 * executing a call on its get(...) method.
 */
${countriesByCode.ES}
${personsByName['Stephen Zucchini'].age}

/*
 * Indexed access to arrays or collections is also performed with brackets, 
 * writing the index without quotes.
 */
${personsArray[0].name}

/*
 * Methods can be called, even with arguments.
 */
${person.createCompleteName()}
${person.createCompleteNameWithSeparator('-')}

表达式基本对象

在上下文变量上评估 OGNL 表达式时,某些对象可用于表达式,以提高灵 Active。这些对象将以#符号开头(根据 OGNL 标准)被引用:

  • #ctx:上下文对象。
  • #vars:上下文变量。
  • #locale:上下文语言环境。
  • #request :(仅在 Web 上下文中)HttpServletRequest对象。
  • #response :(仅在 Web 上下文中)HttpServletResponse对象。
  • #session :(仅在 Web 上下文中)HttpSession对象。
  • #servletContext :(仅在 Web 上下文中)ServletContext对象。

因此,我们可以这样做:

Established locale country: <span th:text="${#locale.country}">US</span>.

您可以在Appendix A中阅读这些对象的完整参考。

表达式 Util 对象

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

  • #execInfo:有关正在处理的模板的信息。
  • #messages:用于获取变量表达式内的外部化消息的方法,与使用#\ {…}语法所获得的方法相同。
  • #uris:转义部分 URL/URI 的方法
  • #conversions:用于执行配置的转换服务(如果有)的方法。
  • #dates:用于java.util.Date对象的方法:格式化,组件提取等。
  • #calendars:类似于#dates,但适用于java.util.Calendar个对象。
  • #numbers:格式化数字对象的方法。
  • #stringsString个对象的方法:包含,startsWith,前置/附加等。
  • #objects:一般对象的方法。
  • #bools:布尔值评估的方法。
  • #arrays:数组的方法。
  • #lists:列表方法。
  • #sets:集合的方法。
  • #maps:Map 方法。
  • #aggregates:用于在数组或集合上创建聚合的方法。
  • #ids:用于处理可能重复的 id 属性的方法(例如,作为迭代的结果)。

您可以在Appendix B中检查这些 Util 对象提供的功能。

重新格式化首页中的日期

现在我们知道了这些 Util 对象,可以使用它们来更改在主页上显示日期的方式。而不是在我们的HomeController中执行此操作:

SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar cal = Calendar.getInstance();

WebContext ctx = new WebContext(request, servletContext, request.getLocale());
ctx.setVariable("today", dateFormat.format(cal.getTime()));

templateEngine.process("home", ctx, response.getWriter());

…我们可以做到这一点:

WebContext ctx = 
    new WebContext(request, response, servletContext, request.getLocale());
ctx.setVariable("today", Calendar.getInstance());

templateEngine.process("home", ctx, response.getWriter());

…然后在视图层本身中执行日期格式化:

<p>
  Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011</span>
</p>

4.3 选择内容的表达方式(星号语法)

变量表达式不仅可以写为${...},还可以写成*{...}

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

什么是选定对象?使用th:object属性的表达式的结果。让我们在用户 Profile(userprofile.html)页面中使用一个:

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

完全等同于:

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

当然,美元和星号语法可以混合使用:

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

选择对象后,选定的对象也可以作为#object表达式变量用于美元表达式:

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

如前所述,如果尚未执行任何对象选择,则美元和星号语法是等效的。

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

4.4 链接网址

由于 URL 的重要性,URL 是 Web 应用程序模板中的一等公民,并且* Thymeleaf Standard Dialect *具有特殊的语法,即@语法:@{...}

URL 有不同类型:

  • 绝对网址:http://www.thymeleaf.org
  • 相对 URL,可以是:
  • 相对页面:user/login.html

    • 上下文相关:/itemdetails?id=3(服务器中的上下文名称将自动添加)
    • 相对服务器:~/billing/processInvoice(允许在同一服务器中的另一个上下文(=应用程序)中调用 URL。
    • 相对协议网址://code.jquery.com/jquery-2.0.3.min.js

这些表达式的实际处理以及它们到将要输出的 URL 的转换是通过org.thymeleaf.linkbuilder.ILinkBuilder接口的实现完成的,这些实现已注册到正在使用的ITemplateEngine对象中。

默认情况下,该接口的单个实现注册为org.thymeleaf.linkbuilder.StandardLinkBuilder类,这对于脱机(非 Web)和基于 Servlet API 的 Web 场景都足够。其他场景(例如与非 ServletAPI Web 框架集成)可能需要链接构建器接口的特定实现。

让我们使用这种新语法。符合th:href属性:

<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" 
   th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>

<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>

<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

这里要注意一些事情:

  • th:href是修饰符属性:处理后,它将计算要使用的链接 URL,并将该值设置为<a>标签的href属性。
  • 我们被允许对 URL 参数使用表达式(如orderId=${o.id}所示)。所需的 URL 参数编码操作也将自动执行。
  • 如果需要几个参数,这些参数将以逗号分隔:@{/order/process(execId=${execId},execType='FAST')}
  • URL 路径中也允许使用变量模板:@{/order/{orderId}/details(orderId=${orderId})}
  • /开头的相对 URL(例如/order/details)将自动以应用程序上下文名称作为前缀。
  • 如果未启用 Cookie 或尚不知道,则可能会将";jsessionid=..."后缀添加到相对 URL 中,以便保留会话。这称为* URL 重写*,Thymeleaf 允许您通过使用 Servlet API 中的response.encodeURL(...)机制为每个 URL 插入自己的重写过滤器。
  • th:href属性允许我们(可选)在模板中具有可使用的静态href属性,因此,当直接打开原型进行原型设计时,模板链接仍可被浏览器导航。

与消息语法(#{...})一样,URL 基也可以是求值另一个表达式的结果:

<a th:href="@{${url}(orderId=${o.id})}">view</a>
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>

主页的菜单

现在,我们知道了如何创建链接 URL,如何在主页中为站点中的其他页面添加一个小菜单?

<p>Please select an option</p>
<ol>
  <li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li>
  <li><a href="order/list.html" th:href="@{/order/list}">Order List</a></li>
  <li><a href="subscribe.html" th:href="@{/subscribe}">Subscribe to our Newsletter</a></li>
  <li><a href="userprofile.html" th:href="@{/userprofile}">See User Profile</a></li>
</ol>

服务器根目录相对 URL

可以使用其他语法来创建相对于服务器根目录的 URL(而不是上下文根目录的 URL),以便链接到同一服务器中的不同上下文。这些网址的指定方式为@{~/path/to/something}

4.5 Fragments

片段表达式是表示标记片段并将其在模板中移动的简便方法。这使我们能够复制它们,并将它们作为参数传递给其他模板,依此类推。

最常见的用途是使用th:insertth:replace进行片段插入(在后面的部分中有更多关于这些的信息):

<div th:insert="~{commons :: main}">...</div>

但是它们可以在任何地方使用,就像其他任何变量一样:

<div th:with="frag=~{footer :: #main/text()}">
  <p th:insert="${frag}">
</div>

在本教程的后面,将有一个完整的章节专门介绍“模板布局”,包括对片段表达式的更深入的说明。

4.6 Literals

Text literals

文本 Literals 只是在单引号之间指定的字符串。它们可以包含任何字符,但是您应该使用\'对其内的任何单引号进行转义。

<p>
  Now you are looking at a <span th:text="'working web application'">template file</span>.
</p>

Number literals

数字 Literals 就是:数字。

<p>The year is <span th:text="2013">1492</span>.</p>
<p>In two years, it will be <span th:text="2013 + 2">1494</span>.</p>

Boolean literals

布尔 Literals 是truefalse。例如:

<div th:if="${user.isAdmin()} == false"> ...

在此示例中,== false写在大括号外,因此 Thymeleaf 负责处理。如果将其写在花括号内,则 OGNL/SpringEL 引擎应负责:

<div th:if="${user.isAdmin() == false}"> ...

空 Literals

nullLiterals 也可以使用:

<div th:if="${variable.something} == null"> ...

Literal tokens

实际上,数字,布尔值和 nullLiterals 是* literal tokens *的一种特殊情况。

这些标记允许在标准表达式中进行一些简化。它们的工作方式与文本 Literals('...')完全相同,但是它们仅允许使用字母(A-Za-z),数字(0-9),方括号([]),点(.),连字符(-)和下划线(_)。因此,没有空格,没有逗号等。

好的部分?令牌不需要任何引号。因此,我们可以这样做:

<div th:class="content">...</div>

instead of:

<div th:class="'content'">...</div>

4.7 附加 Literals

文本,无论是 Literals 还是评估变量或消息表达式的结果,都可以使用+运算符轻松附加:

<span th:text="'The name of the user is ' + ${user.name}">

4.8Literals 替代

Literals 替换可以轻松格式化包含变量值的字符串,而无需在 Literals 后面加上'...' + '...'

这些替换项必须用竖线(|)包围,例如:

<span th:text="|Welcome to our application, ${user.name}!|">

等效于:

<span th:text="'Welcome to our application, ' + ${user.name} + '!'">

Literals 替换可以与其他类型的表达式结合使用:

<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">

|...|Literals 替换内仅允许变量/消息表达式(${...}*{...}#{...})。没有其他 Literals('...'),布尔/数字标记,条件表达式等。

4.9 算术运算

一些算术运算也可用:+-*/%

<div th:with="isEven=(${prodStat.count} % 2 == 0)">

请注意,这些运算符也可以在 OGNL 变量表达式内部应用(在这种情况下,将由 OGNL 代替 Thymeleaf 标准表达式引擎执行):

<div th:with="isEven=${prodStat.count % 2 == 0}">

请注意,其中一些运算符存在文本别名:div(/),mod(%)。

4.10 比较器和equal

表达式中的值可以与><>=<=符号进行比较,并且==!=运算符可以用于检查是否相等(或是否相等)。请注意,XML 规定<>符号不应在属性值中使用,因此应将它们替换为<>

<div th:if="${prodStat.count} > 1">
<span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">

一个更简单的替代方法可能是使用以下某些运算符存在的文本别名:gt(>),lt(<),ge(>=),le(<=),not(!)。也是eq(==),neq/ne(!=)。

4.11 条件表达式

条件表达式旨在仅根据评估条件的结果来评估两个表达式之一(本身就是另一个表达式)。

让我们看一个示例片段(引入另一个* attribute 修饰符* th:class):

<tr th:class="${row.even}? 'even' : 'odd'">
  ...
</tr>

条件表达式的所有三个部分(conditionthenelse)本身都是表达式,这意味着它们可以是变量(${...}*{...}),消息(#{...}),URL(@{...})或 Literals('...')。

也可以使用括号嵌套条件表达式:

<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">
  ...
</tr>

其他表达式也可以省略,在这种情况下,如果条件为 false,则返回 null 值:

<tr th:class="${row.even}? 'alt'">
  ...
</tr>

4.12 默认表达式(Elvis 运算符)

默认表达式是一种特殊的条件值,没有* then 部分。它等效于 Groovy 之类的某些语言中的 Elvis 运算符*,可让您指定两个表达式:如果第一个表达式的计算结果不为 null,则使用第一个表达式,但是如果第二个表达式使用,则使用第二个表达式。

让我们在用户 Profile 页面中看到它的实际效果:

<div th:object="${session.user}">
  ...
  <p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p>
</div>

如您所见,运算符为?:,并且仅当求值*{age}的结果为 null 时,才在此处使用其指定名称的默认值(在这种情况下为 Literals 值)。因此,这等效于:

<p>Age: <span th:text="*{age != null}? *{age} : '(no age specified)'">27</span>.</p>

与条件值一样,它们可以在括号之间包含嵌套表达式:

<p>
  Name: 
  <span th:text="*{firstName}?: (*{admin}? 'Admin' : #{default.username})">Sebastian</span>
</p>

4.13 The No-Operation token

No-Operation 令牌由下划线符号(_)表示。

此令牌背后的想法是指定表达式的期望结果是“不执行任何操作”,即完全像处理属性(例如th:text)根本不存在一样。

除其他可能性外,这还使开发人员可以将原型文本用作默认值。例如,代替:

<span th:text="${user.name} ?: 'no user authenticated'">...</span>

…我们可以直接使用*’no user authenticated’*作为原型文本,这样从设计的角度来看,代码既简洁又通用:

<span th:text="${user.name} ?: _">no user authenticated</span>

4.14 数据转换/格式化

Thymeleaf 为变量(${...})和选择(*{...})表达式定义了双括号语法,该语法使我们能够通过配置的转换服务来应用数据转换

它基本上是这样的:

<td th:text="${{user.lastAccessDate}}">...</td>

注意到那里有双括号了吗:${{...}}。指示 Thymeleaf 将user.lastAccessDate表达式的结果传递给转换服务,并要求它执行 格式化操作 (转换为String),然后再写入结果。

假设user.lastAccessDate的类型为java.util.Calendar,如果已注册转换服务(IStandardConversionService的实现)并包含Calendar -> String的有效转换,则将应用它。

IStandardConversionService(StandardConversionService类)的默认实现只是对转换为String的任何对象执行.toString()。有关如何注册自定义转换服务实现的更多信息,请查看有关配置的更多信息部分。

官方的 thymeleaf-spring3 和 thymeleaf-spring4 集成软件包将 Thymeleaf 的转换服务机制与 Spring 自己的* Conversion Service *基础结构透明地集成在一起,以便在 Spring 配置中声明的转换服务和格式化程序将自动提供给${{...}}*{{...}}表达式。

4.15 Preprocessing

除了用于表达式处理的所有这些功能之外,Thymeleaf 还具有预处理表达式的功能。

预处理是在普通表达式之前执行的表达式的执行,该表达式允许修改最终将要执行的表达式。

预处理表达式与普通表达式完全一样,但是会出现双下划线符号(例如__${expression}__)。

假设我们有一个 i18n Messages_fr.properties条目,其中包含一个 OGNL 表达式,该表达式调用特定于语言的静态方法,例如:

article.text=@myapp.translator.Translator@translateToFrench({0})

…和Messages_es.properties equivalent

article.text=@myapp.translator.Translator@translateToSpanish({0})

我们可以创建标记片段,该片段根据语言环境评估一个表达式或另一个表达式。为此,我们将首先选择表达式(通过预处理),然后让 Thymeleaf 执行它:

<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>

请注意,法语语言环境的预处理步骤将创建以下等效项:

<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>

可以使用\_\_在属性中对预处理字符串__进行转义。

赞(0) 打赏
版权归原创作者所有,任何形式转载请联系我们:大白菜博客 » 四、标准表达语法

评论 抢沙发

5 + 7 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏