Thymeleaf学习三
4.Standard Expression Syntax
我们将学习Thymeleaf Standard Dialect中最重要的部分:Thymeleaf Standard Expression syntax
我们已经见过两种合法的属性值以下面的语法表示:message和变量表达式
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
<p>Today is: <span th:text="${today}">13 february 2011</span></p>
但是事实上有更多的表达式:
-
简单表达式
1.变量表达式 ${…}
2.选择变量表达式 *{…}
3.message 表达式 #{…}
4.链接表达式 @{…}
5.片段表达式 ~{…} -
文本
1.text文本 ‘one text’,‘Another text’,…
2.数字文本 0,1,2,3.21
3.布尔文本
4.文本标记one ,sometext,main -
算术操作
1.二进制操作符 +.-,*,/,%
2.负号 : -
-布尔操作
1.二进制操作符 and,or
2.布尔否定 (一元操作符): !, not
-
比较和相等
1.比较 >,<,>=,<=(gt,lt,ge,le)
2.相等操作 ==, !=(eq,ne) -
条件操作符
1.if-then (if) ? (then)
2.if-then-else (if) ? (then):(else)
3.Default (value) ?: (defaultValue) -
特殊标记
无操作 _
所有这些都可以被组合起来和嵌套使用
'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))
4.1Messages
#{…}允许我们使用message表达式:
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
到:
home.welcome=¡Bienvenido a nuestra tienda de comestibles!
但是有一个方面我们没有考虑到,如果message的内容不是完全静态的该怎么办呢?
比如说,我们的应用知道谁是访问网站的用户,并且我们希望使用他们的姓名
<p>¡Bienvenido a nuestra tienda de comestibles, John Apricot!</p>
这就意味着我们希望加一个参数到我们的message:
home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!
参数根据 java.text.MessageFormat 标准语法确定就可以,意味着你可以格式化到数字和日期,正如java.text.*包里面的类一样。
如果想给我们的参数制定一个值,在有一个HTTP sessin属性user的情况下,我们可以这样写:
<p th:utext="#{home.welcome(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
可以指定很多的属性,中间使用逗号隔开
message key可以来自一个变量
<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">
Welcome to our grocery store, Sebastian Pepper!
</p>
4.2变量
我们之前有提到过,${…}表达式实际上就是OGNL表达式在包含在context里面的变量集合上执行的结果。
从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 name通过执行:
((User) ctx.getVariable("session").get("user")).getName();
但是get方法导航知识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('-')}
基本对象表达式
当我们检查在context变量上的OGNL表达式的时候,有一些对象创建了来使得表达式更具有灵活性。这些表达式可以使用 # 来引用
- #ctx context对象
- #vars context变量
- #locale context locale
- #request (仅在web context)HttpServletRequest对象
- #response (仅在web context)HttpServletResponse对象
- #session (仅在web context)HttpSession对象
- #servletContext (仅在web context)serveletContext对象
所以我们可以这样做:
Established locale country: <span th:text="${#locale.country}">US</span>.
应用对象表达式
除了这些基本对象,Thymeleaf也给我们提供了一些应用对象来帮助我们完成一些通用的任务
#execInfo: information about the template being processed.
#messages: methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.
#uris: methods for escaping parts of URLs/URIs
#conversions: methods for executing the configured conversion service (if any).
#dates: methods for java.util.Date objects: formatting, component extraction, etc.
#calendars: analogous to #dates, but for java.util.Calendar objects.
#numbers: methods for formatting numeric objects.
#strings: methods for String objects: contains, startsWith, prepending/appending, etc.
#objects: methods for objects in general.
#bools: methods for boolean evaluation.
#arrays: methods for arrays.
#lists: methods for lists.
#sets: methods for sets.
#maps: methods for maps.
#aggregates: methods for creating aggregates on arrays or collections.
#ids: methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).
重新格式化我们的主页日期
与其这样写
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());
然后在view层做日期的格式化:
<p>
Today is: <span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011</span>
</p>
4.3 选择表达式
变量不仅可以写成 ${},也可以写成 *{}
什么是选择的对象呢?一个使用th:object属性的表达式的结果。
让我们使用我们的用户资料页(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:
- 绝对的URL:http://www.thymeleaf.org
- 相对的URL,可以是:
1.相对于页的 user/login.html
2.相对于内容的 : /itemdetails?id=3 (在服务器中会自动加上context的名字)
3.相对于server的 : ~/billig/processInvoice (允许在另一个context中call URL)
4.相对于协议的: //code.jquery.com/jquery-2.0.3.min.js
这些表达式和他们的转换长URL是通过实现org.thymeleaf.linkbuilder.ILinkBuilder接口注册到正在使用的ITemplateEngine对象。
默认的,这个接口的一个单独实现被注册到org.thymeleaf.linkbuilder.StandardLinkBuilder,足够应付离线和web场景基于Servlet API。其他的场景(如非ServeletAPI 框架的集成)也可能需要link builder接口的实现。
认识一下 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,并且设置那个属性到标签的href属性
- 我们可以对URL参数使用表达式,如(orderId=${o.id})。需要的URL参数编码会自动进行
- 如果需要好几个参数,使用逗号分隔:@{/order/process(execId=${execId},execType=‘FAST’)}
- 也可以使用变量模板 :@{/order/{orderId}/details(orderId=${orderId})}
- 相对URL以 / 开始,如(/order/details)将会被自动加上context名字的前缀
- 如果没由使能cookie或者不知道,可以加上;jsessionid=…后缀,这样的话session就可以被保留,这个叫做URL ReWriting.Thymeleaf允许你插入你自己的过滤器通过从Servlet API和每个URL使用response.encodeURL(…)机制.
- th:href允许我们在我们的模板里有一个静态的href属性,这样的话,我们的模板为了原型目的使用浏览器打开的时候可以变得直观。
由于message语法#{…},URL也可以成为检查其他语法的结果
<a th:href="@{${url}(orderId=${o.id})}">view</a>
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>
主页的菜单
<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
@{~/path/to/something}
4.5 分段表达式
分段表达式是一个描述markup片段的简单方法。这让我们可以很容易地作为参数复制,传递到其他的模板。
分段表达式最常用的插入方法是使用 th:insert 和th:repalce(更多一点)
<div th:insert="~{commons :: main}">...</div>
可以在任何地方使用,就像变量一样
<div th:with="frag=~{footer :: #main/text()}">
<p th:insert="${frag}">
</div>
4.6字面表达式
text
<p>
Now you are looking at a <span th:text="'working web application'">template file</span>.
</p>
number
<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
<div th:if="${user.isAdmin()} == false"> ...
注意,false写在括号外面,若写在里面就是OGNL/Spring EL的责任了
<div th:if="${user.isAdmin() == false}"> ...
null
<div th:if="${variable.something} == null"> ...
Literal tokens
number,boolean 和null字面量实际上都是Literal tokens的特例。
不需要括号
<div th:class="content">...</div>
不需要
<div th:class="'content'">...</div>
4.7追加文本
使用+操作符
<span th:text="'The name of the user is ' + ${user.name}">
4.8字面替换
必须使用 |
<span th:text="|Welcome to our application, ${user.name}!|">
等效于:
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
可以和其他表达式一起使用:
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
4.9算术表达式
<div th:with="isEven=(${prodStat.count} % 2 == 0)">
注意,上面的操作符也可以在OGNL里面执行
<div th:with="isEven=${prodStat.count % 2 == 0}">
4.10比较和相等
<div th:if="${prodStat.count} > 1">
<span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">
4.11条件表达式
介绍一个新属性 th:class
<tr th:class="${row.even}? 'even' : 'odd'">
...
</tr>
else表达式也可以被省略,这样的话,如果条件位假就返回null
<tr th:class="${row.even}? 'even' : 'odd'">
...
</tr>
4.12 默认表达式
<div th:object="${session.user}">
...
<p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p>
</div>
操作符是?: ,在这里我们制定了一个值为name,当且仅当*{age}结果是null
等效于:
<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 非操作符标记
_
表明,这个表达式是啥也不干
与其
<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表达式的结果给转换服务,并要求其在写入结果之前进行格式化操作。
假设user.lastAccessDate是java.util.Calendar类型,如果转换服务(IStandardConversionService的实现)已经被注册并且包含一个从Calendar到String的合法转换,那么其将会被应用。
IStandardConversionService 的默认实现(StandardConversionService 类)仅仅执行了toString()在任何转换到String的对象上。
4.15预处理
除了这些表达式处理的特点,Thymelead也有预处理表达式的特点
预处理是一个允许表达式修改之前的并会被最终执行的正常表达式的表达式。
很像正常的表达式,只是会在前后加上双下划线,例如(${expression})
我们看一个i18n 的Message_fr.properties入口,包含一个叫做针对语言的静态方法的OGNL表达式,比如:
article.text=@myapp.translator.Translator@translateToFrench({0})
还有Messages_es.properties的等效的:
article.text=@myapp.translator.Translator@translateToSpanish({0})
我们可以创建一个markup片段,来评估一个取决于locale的表达式,我们首先选择表达式(通过预处理)然后让Thymeleaf执行。
<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>
注意对于一个法国的locale的预处理会创建下面的等效:
<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>
预处理的String __可以在属性中使用__转义。