http://www.thymeleaf.org/doc/articles/layouts.htmljavascript
thymeleaf的初次使用(带参请求以及调用带参js方法)css
以前对于前端框架接触较少,第一次接触thymeleaf,虽然说看起来并不复杂但我仍是花费了好一下子才弄懂。html
话很少少下面就简单说一下我在项目中的应用。前端
首先是java代码 controller层 将须要在前端展现的信息放入model中:html5
@RequestMapping("getAll") public String getAll(Model model){ List<ScheduleJob> list = scheduleJobService.getAllJob(); model.addAttribute("list", list); return "sch/quartz/list"; }
以后即是在前端html页面的遍历java
1 <tr th:each="job:${list}">
2 <td th:text="${job.name }">任务名</td>
3 <td th:text="${job.group }">任务组</td>
4 <td th:text="${job.status }">状态</td>
5 <td th:text='${job.cronExpression }'></td>
6 <td th:text="${job.className }">类名</td>
7 <td th:text="${job.description }">描述</td>
8 <td><a href="#" th:href="@{/quartz/pause(name=${job.name},group=${job.group})}">暂停</a></td>
9 <td><a href="#" th:href="@{/quartz/resume(name=${job.name},group=${job.group})}">恢复</a></td>
10 <td><a href="#" th:href="@{/quartz/run(name=${job.name},group=${job.group})}">执行一次</a></td>
11 <td><a href="#" th:href="@{/quartz/delete(name=${job.name},group=${job.group})}">删除</a></td>
12 <td><button id="edit" th:name="${job.name}" th:id="${job.group}" onclick="test(this)">修改cron表达式</button></td>
13 </tr>
这里直接在<tr>标签中 用 th:each 放入须要遍历的内容,以及定义变量名;在<td>标签中用th:text来展现内容。mysql
在<a>标签中用 普通的href不能实现带参的接口请求,因此须要使用 th:href 的这种语法来实现带参的接口请求,参数用()跟在后面就能够。jquery
至于调用js的带参方法就须要用12行那种办法 将参数做为 th 标签的name或者id传入方法中,具体的js方法以下;git
1 function test(obj){
2 var cron = $("#cron").val();
3 var name = $(obj).attr("name");
4 var group = $(obj).attr("id");
5 $.post("edit",{"name":name,"group":group,"cron":cron},function(){
6 alert("更新成功!!!");
7 window.location.reload();
8 });
9 }
这样就能实现js带参方法的调用,目前来讲我所知道的能够实现的就是这种方式。github
补充一点,前端遍历的实体类须要重写toString方法,而且必须是以下格式的:
1 @Override 2 public String toString() { 3 StringBuilder builder = new StringBuilder(); 4 builder.append("{name:\""); 5 builder.append(name); 6 builder.append("\", group:\""); 7 builder.append(group); 8 builder.append("\", cronExpression:\""); 9 builder.append(cronExpression); 10 builder.append("\", status:\""); 11 builder.append(status); 12 builder.append("\", description:\""); 13 builder.append(description); 14 builder.append("\", className:\""); 15 builder.append(className); 16 builder.append("\"}"); 17 return builder.toString(); 18 }
这样才能在页面上成功遍历。
对于thymeleaf,目前我也就掌握了这些简单的使用。
http://www.cnblogs.com/stan-tiger/p/5622824.html
spring security + thymeleaf 判断登陆用户的权限
spring security的UserDetailService是我本身定义的。
@Component public class MyUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) { // Get user and his authority from userRepository
user = userRepository.getUserByUsername(username);
Set<GrantedAuthority> authorities = new HashSet<>(); user.getPermissions().forEach(r -> authorities.add(new SimpleGrantedAuthority(r.getValue())));
MyUser myUser = new MyUser (username, ....); return myUser; }}
MyUser.java,扒的User.java
public class MyUser implements UserDetails, CredentialsContainer {...}
index.html:
<script th:inline="javascript">
/*<![CDATA[*/
var username = /*[[${name}]]*/ '';
var isAnonymous = [[${#authorization.expression('hasRole(''ROLE_ANONYMOUS'')')}]];
var isSomeAuth = [[${#authorization.expression('hasRole(''SOME_AUTH'')')}]];
var s = [[${#authorization.expression('hasRole(''ROLE_SOME_AUTH'')')}]];
从数据库获取的用户Permission,在前端怎么都拿不到,返回的结果一直false,可是经过${authentication.principal}打印的结果却可以看到。
debug发现expression方法会调用org.springframework.security.access.expression.SecurityExpressionRoot的方法hasAnyRole,这个方法会将hasRole()里的字符串加上ROLE_与UserDetails的GrantedAuthority逐个比较,所以,最简单的方法就是把数据库中权限用ROLE_开头
http://blog.csdn.net/kakadiablo/article/details/59108583
Thymeleaf是网站或者独立应用程序的新式的服务端java模板引擎,能够执行HTML,XML,JavaScript,CSS甚至纯文本模板。
Thymeleaf的主要目标是提供一个以优雅的高可维护型的方式建立模板,为了达到这个目的,他创建了天然模板的概念,以一种不影响模板设计原型的方式将逻辑注入到模板文件中,能够显著的减小设计和开发之间的沟通成本。
在须要的状况下,Thymeleaf能够建立一个各类校验模式的模板(尤为是基于HTML5)
Thymeleaf 容许使用六种模板,每一个被称为一种模板模式:
这其中有两种标记语言模板模式(HTML,XML),三种文本语言模板模式(TEXT,JAVASCRIPT,CSS),和一种无操做模板模式(RAW)
其中:
HTML模板模式可使用任何HTML输入,包括HTML5,HTML4或XHTML,全验证或无验证都将能够被执行,模板中的代码和结构将被尽最大可能性的解析输出。
XML模板模式将被用来执行XML的输入,在这种模式下,若是被要求严格验证,则好比没有闭合标签,属性没有引号等,都将输出异常,注意一点是,无验证(经过DTD或XMLSchema)仍然是能够执行的。
文本模板模式将可使用一种非标记性的特殊语法。它多是一个电子邮件的模板或者文本模板等模板文件。注意:HTML模式或XML模式也可使用文本模板模式处理,但在这种状况下,他们不会被解析为标记语言,每一个标记,DOCTYPE,comment等都将视为单纯的文本。
JavaScript模板模式可使用Thymeleaf处理Js文件。这意味着它可以在JS文件中以HTML文件一样的方式使用数据模型。但它还整合了一些JS的专有属性,如一些特定编码和脚本语言。JavaScript模板是一种支持特殊语法的文本模板模式。
CSS模板模式和JavaScript模板模式相似,便可以使用Thymeleaf处理CSS文件,它也是用支持特殊语法的文本模板模式。
RAW模板模式是一种非执行模板,他用了处理一些保持原样的资源(如文件,某Url的响应等),好比,在一些格式以外,可能会有一些信息须要原封不动的展示出来,这些应该明确的告知Thymeleaf不要讲它们进行执行。
Thymeleaf 是一种可扩展的模板引擎(其实应该称呼为模板引擎框架),它容许您自由的定义制做一个能够精确到任何程度的模板。
一个对象,运用一些逻辑(如标签,文本,注释,或仅仅是一个占位符),被称为处理器,这些处理器加一些额外的东西,被称为方言。其中Thymeleaf的核心库提供了一个一目了然的方言被称为标准方言,他应该可以知足大多数用户的需求。
应明确一点就是,方言实际上能够是没有处理器,彻底有其余的类型的东西注册,但处理器是一种最多见的使用方式。
本教程即便用标准方言,你能够在下面页中了解这个方言的每个属性和语法功能的定义,即便他没有明确说起。
固然,若是用户想要定义本身的处理逻辑,和使用库中的各类先进功能,那么,能够定义本身的方言(甚至扩展标准方言),而模板引擎能够一次配置多种方言。
官方thymeleaf-spring3和thymeleaf-spring4集成包都定义了另外一种方言被称为“String标准方言”,大多数至关于标准方言,但有一小部分为了适应Spring框架的某些功能(如利用Spring的EL表达式而不是Thymeleaf标准的OGNL)因此,若是你是一个Spring mvc用户,请你不要在浪费时间抓紧学习,由于那你在这里学习的全部东西,都讲在你的Spring项目中使用
Thymeleaf标准方言能够在任何模式的模板中使用,尤为适合面向web的模板模式(如HTML5和XHTML),除了HTML5,他能够支持和验证一下的XHTML格式:XHTML 1.0 Transitional, XHTML 1.0 Strict, XHTML 1.0, 和 XHTML 1.1.
标准方言的大多数处理器都是属性处理器。他们在浏览器处理HTML模板文件以前,他会简单处理额外的属性,如,在jsp中使用标签库能够包括代码段,但代码段浏览器并不会直接显示同样:
<form:inputText name="userName" value="${user.name}" />
Thymeleaf中,能够实现一样的功能:
<input type="text" name="userName" value="niufennan" th:value="${user.name}" />
这个会在浏览器中被正确的显示,同时,也允许咱们指定一个默认值(可选)("niufennan"),当静态文件在浏览器中打开的时候,显示的是Thymeleaf使用模板中${user.name}的值对value中的值进行了替换的结果。
这将可使设计人员和开发人员使用几乎相同的模板文件,而且可使用极少的工做,就使一个静态文件转换为开发模板文件。有这样能力的模板一般称之为天然模板
当前及将来示例代码下载地点
为了更好的说明Thymeleaf的各类概念,教程所使用的Demo能够从项目网站下载。
这个项目是一个假想的购物网站,将提供足够的场景来演示Thymeleaf的各类不一样的特色。
这个购物网站须要一个很是简单的实体模型,产品(Products)销售给客户(Customers),同时建立订单(Orders),而且,还须要咱们管理用户对产品的评论(Coomments)
一个简单的服务层:
public class ProductService { ... public List<Product> findAll() { return ProductRepository.getInstance().findAll(); } public Product findById(Integer id) { return ProductRepository.getInstance().findById(id); } }
最后,web层将有一个过滤器,用来根据请求的url来执行Thymeleaf
private boolean process(HttpServletRequest request, HttpServletResponse response) throws ServletException { try { //静态资源不使用框架 if(request.getRequestURI().startsWith("/css")|| request.getRequestURI().startsWith("/images")|| request.getRequestURI().startsWith("/favicon") ) { return false; } /* * 根据controller/url的映射规则,获取将要执行的控制权的request * 若是没有控制权可用,返回false,让其余的filter或者servlet来执行 */ IGTVGController controller = application.getTemplateEngine().resolveControllerForRequest(request); if (controller == null) { return false; } /* * 获取 TemplateEngine 实例. */ ITemplateEngine templateEngine = application.getTemplateEngine(); /* * 写 response headers */ response.setContentType("text/html;charset=UTF-8"); response.setHeader("Pragma", "no-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); /* * 执行控制权并处理模板 * 将结果写入response */ controller.process( request, response, this.servletContext, templateEngine); return true; } catch (Exception e) { throw new ServletException(e); } }
还有IGTVGController 接口
public interface IGTVGController { public void process( HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, ITemplateEngine templateEngine) throws Exception; }
如今要作的就是IGTVGController接口的实现,主要实现了从服务中检索数据和处理模板使用的ITemplateEngine 对象
最后,他的效果图以下
可是,首先让咱们看看模板引擎是如何初始化的。
在过滤器process方法中加入以下的代码:
ITemplateEngine templateEngine = application.getTemplateEngine();
这意味着GTVGApplication类中在加载的时候建立和配置了Thymeleaf启动应用的最重要对象:模板引擎实例(ITemplateEngine接口的实现)
咱们的org.thymeleaf.TemplateEngine对象的初始化就像是这样:
public class GTVGApplication { ... private static TemplateEngine templateEngine; ... public GTVGApplication(ServletContext servletContext) { ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext); //有String类型重载 templateResolver.setTemplateMode(TemplateMode.HTML); // 这段将把 "home" 转换为 "/WEB-INF/templates/home.html" templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); //模板的缓存的生存时间默认为1小时,若是没有设置,将被缓存到LRU(缓存淘汰算法)中 templateResolver.setCacheTTLMs(3600000L); //缓存默认为true,若但愿修改模板后自动更改(如调试时),可设置为false templateResolver.setCacheable(true); templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver); } ... }
固然,配置TemplateEngine对象的方式有不少,但这几行代码将让咱们学会足够的必要步骤。
从模板解释器开始:
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
模板解释器对象从以下接口实现:
org.thymeleaf.templateresolver.ITemplateResolver:
public interface ITemplateResolver { ... /* * 模板经过名字或内容来解析。星期若是他有主模板的状况下还将尝试将此解析为另外一个模板的片断 * 若是这个模板不能经过解析,将返回null. */ public TemplateResolution resolveTemplate( final IEngineConfiguration configuration, final String ownerTemplate, final String template, final Map<String, Object> templateResolutionAttributes); }
有这些解释器对象决定咱们的模板在GTVG应用中将被如何访问,并在org.thymeleaf.templateresolver.ServletContextTemplateResolver中实现,咱们将在Servlet的上下文中检索制定的模板文件资源。Servlet上下文指的是javax.servlet.ServletContext对象,他普遍的在每个java web应用中存在。并将使用web应用程序的根做为资源文件的根路径。
但这不能说全部的模板都如此解析,由于咱们能够给他设置一些配置参数。首先,模板模式中,其中一个标准为:
templateResolver.setTemplateMode("HTML");
HTML是ServletContextTemplateResolver的默认模板模式,
但出于代码可读性仍是显示设置。
templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html");
顾名思义,这是模板路径的前缀和后缀,经过模板的名称,能够将真实的路径传递到引擎来使用它。
使用这个配置:模板名称"product/list"的路径将为:
servletContext.getResourceAsStream("/WEB-INF/templates/product/list.html")
经过配置为cachtTTLMS属性配置一个总的时间量,能够配置模板解释器在缓存中存活的时间:
templateResolver.setCacheTTLMs(3600000L);
固然,若是缓存达到了最大缓存,而且此模板是最先的缓存条目,那么在达到TTL以前,他仍是有可能被清理出缓存的。
缓存的行为和大小,均可以由用户本身定义,既能够实现ICacheManager接口本身实现规则,也能够经过修改StandardCacheManager对象设置缓存默认管理规则。
咱们将在之后学习模板解释器,如今让咱们来看看模板引擎对象。
模板引擎对象是接口org.thymeleaf.ITemplateENgine的实现,Themeleaf的core包提供一个默认的实现:org.thymeleaf.TemplateEngine,下边是建立引擎的一个例子:
templateEngine = new TemplateEngine(); templateEngine.setTemplateResolver(templateResolver);
很简单,是吧,咱们所须要的仅仅是建立一个实例并设置一个模板解释器。
一个模板解释器是一个TemplateEngine必须的参数,固然,还须要其余的参数,将在后边介绍(如消息解释器,缓存大小等),如今,这些已经知足咱们须要了。
咱们的模板引擎如今已经能够启动,并可使用Thymeleaf建立咱们的页面了。
咱们的第一个任务是为咱们的商店网站建立一个首页。
第一个版本,将是一个很是简单的页面:仅仅有个title和一个欢迎信息。在/WEB-INF/template/home.html文件:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Good Thymes Virtual Grocery</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" media="all" href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" /> </head> <body> <p th:text="#{home.welcome}">欢迎您光临本店!</p> </body> </html>
首先你会注意到,这个文件是HTML5标准的DOCTYPE格式,他能够在主流浏览器中正确的显示(浏览器会忽略掉一下他不认识的属性,如th:text)。
可是,你也可能注意到,这个模板并非一个彻底有效的HTML5文档,由于有HTML5规格中没有的非标准属性,如th:*属性,事实上,咱们添加了一个xmlns:th属性在html标签,用来分类非html5的标记或属性。
<html xmlns:th="http://www.thymeleaf.org">
那么,如何使用一个彻底符合HTML5标准的模板呢?也很容易,使用Thymeleaf数据属性语法便可,它使用data-前缀的方式来使用:
<!DOCTYPE html> <html> <head> <title>Good Thymes Virtual Grocery</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" media="all" href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" /> </head> <body> <p data-th-text="#{home.welcome}">欢迎您光临本店!</p> </body> </html>
自定义的data-前缀属性是html5彻底支持的语法,这样,上面这段代码就是一个彻底符合HTML5定义的文档
这两种语法是彻底等价的,能够无缝替换,但为了使代码更为简洁,本教程使用命名空间符号(th:),另外,th:语法是更经常使用的语法,在每一种Thymeleaf模板模式都是可使用,但data-这种方式只能在HTML模式中使用。
外部文本一般是模板文件中须要替换的代码,一般保存在外部的独立文件(一般是特定的.properties文件)中。他们能够很容易的设置其余语言的等效文本(这一过程一般称为国际化)。这些外在的文本片断一般被称为"messages"
Messages须要一个惟一性的Key进行标识,Thymeleaf容许你经过指定一个#{...}语法格式的Message对应一个具体的文本。如:
<p th:text="#{home.welcome}">欢迎您光临本店!</p>
咱们能够看到Thymeleaf标准方言的两个主要的语言点:
th:text属性,他声明设置表达式的值,并使表达式返回的值来填充标签内容,替换或设置标签内部的内容,当前例子中即替换“欢迎光临本店”这些字。
#{home.welcome}表达式,一个标准的表达式语法,指出在模板中,th:text属性说对应Message的key,即便用home.welcome对应的value替换现有内容。
如今,对应的外部文本在哪?
在Thymeleaf中这些文件的位置都是可配的,他经过实现org.thymeleaf.messageresolver.IMessageResolver接口进行配置,一般会实现一个基于.properties文件的实现,固然也一样能够根据实际状况进行自定义实现,如将Message存储在数据库中。
若是咱们在模板引擎初始化的时候没有配置任何消息解释器的实现,那就意味着咱们使用的是默认的一个消息解析实现,实现类为:org.thymeleaf.messageresolver.StandardMessageResolver.
这个消息解释器会扫描/WEB-INF/template/home.html相同目录下的相同名称的.properties文件,好比:
其中的一个properties文件以下(en):
home.welcome=Welcome to our grocery store!
咱们已经完成了咱们所须要的Thymeleaf框架的模板,接下来建立Home的Controller。
为了处理模板,咱们将建立一个实现了以前看到的IGTVTController接口的HomeController类:
public class HomeController implements IGTVGController { @Override public void process(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, TemplateEngine templateEngine) throws Exception { // TODO Auto-generated method stub WebContext ctx=new WebContext(request,response,servletContext, request.getLocale()); templateEngine.process("home", ctx,response.getWriter()); } }
能够看到,第一件事就是建立了一个context,一个实现了org.thymeleaf.context.IContext接口的Thymeleaf上下文。上下文应该包含了模板引擎所执行全部所需数据的变量的map,而且引用了Locale所需的外部文件。
public interface IContext { public Set<String> getVariableNames(); public Locale getLocale(); public boolean containsVariable(final String name); public Object getVariable(final String name); }
而且扩展了一个专用接口,org.thymeleaf.context.IWebContext,用于基于ServletApi的应用使用,如SpringMVC:
public interface IWebContext extends IContext { public HttpServletRequest getRequest(); public HttpServletResponse getResponse(); public HttpSession getSession(); public ServletContext getServletContext(); }
Thymeleaf核心库为每一个接口给出了一个实现:
正如在controller的代码中看到的那样,咱们使用了WebContext,事实上咱们也必须使用它,由于这是ServletContextTemplateResolver必须使用一个IWebContext的实现类。
WebContext ctx = new WebContext(request, response,servletContext, request.getLocale());
这个构造函数的四个参数中有三个是必须的,第四个参数系统能够设置一个默认的本地区域,若是没有设置,则使用系统默认。
从WebContext还提供了一些专用的表达式,能够在模板中得到request的参数,request,session,application的属性等,好比:
就在执行前,一个特殊的变量被设置为包含了Context和WebContext的全context对象(即全部实现了IContext的对象),被称为执行信息(execInfo),这个变量有两个您能够从模板中使用的数据。
随着context对象已经准备好,剩下只须要指定模板名称和context,以执行模板引擎,兵传递给response的writer,以写入响应。
templateEngine.process("home", ctx, response.getWriter());
看看执行结果(图):
home.welcome=欢迎光临这个<b>超级棒</b>的商店!
若是是这样的,输出结果为:
home.welcome=欢迎光临这个<b>超级棒 </b>的商店!
很显热,这并非我但愿看见的,咱们但愿b做为一个标签来处理,而不是转义后显示在浏览器上。
这是浏览器对th:text的默认行为。若是咱们但愿Thymeleaf直接使用html标签,而不是转义他们,咱们可使用不一样的属性th:utext("unescaped text")
<p th:utext="#{home.welcome}">欢迎您光临本店!</p> 这样输出信息就是所须要的了: <p>欢迎光临这个<b>超级棒</b>的商店!</p>
thymeleaf 回显富文本编辑器的内容
把 th:text 换成 th:utext 便可
th:text属性
可对表达式或变量求值,并将结果显示在其被包含的 html 标签体内替换原有html文本
文本链接:用“+”符号,如果变量表达式也能够用“|”符号
e.g.
若home.welcome=Welcome to our <b>fantastic</b> grocery store!
用<p th:text="#{home.welcome}"></p>解析结果为:
<p>Welcome to our <b>fantastic</b> grocery store!</p>
2 th:utext属性
解决方案
<p th:utext="#{home.welcome}"></p>便可。
Welcome to our fantastic grocery store!
等效于html :<p>Welcome to our <b>fantastic</b> grocery store!</p>
若是我想在首页多现实一下信息,好比显示日期,就像下边这样:
欢迎这个超级棒的网店 当前日期为:2016-9-2
那么首先,咱们应该回到首页,添加当前时间到context变量:
WebContext ctx=new WebContext(request,response,servletContext, request.getLocale()); //时间 SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd"); Calendar cal=Calendar.getInstance(); ctx.setVariable("today",sdf.format(cal.getTime())); templateEngine.process("home", ctx,response.getWriter());
咱们已经添加了一个String变量在context中,如今修改模板:
<p th:utext="#{home.welcome}">欢迎您光临本店!</p> <p>当前日期为: <span th:text="${today}">2016年9月5日</span></p>
能够看到,咱们仍然使用th:text属性,可是语法稍有不一样,此次使用的是${...}而不是#{...},这是一个用来显示变量值的表达式。它使用OGNL语言来映射context中的变量。
\({today}表达式只是简单的表示为:获取一个叫today的变量,可是这个表达式也能够变得很复杂,如\){user.name}的意思是获取一个user变量,并调用他的getName()方法。
属性值有至关多的可能性,如:消息,变量表达式等等,下一章将展现这些都是什么.
在将这个小demo发展成一个真正的网店以前,先休息一下,熟悉一下Thymeleaf标准方言中最重要的组成部分:Thymeleaf标准表达式语法。
在以前,已经看到过两种有效的属性表达式:消息和变量表达式:
<p th:utext="#{home.welcome}">欢迎您光临本店!</p> <p>当前日期为: <span th:text="${today}">2016年9月5日</span></p>
但还有更多的类型和有趣的细节,咱们都还不知道,下面咱们首先来一个标准表达式的快速总结:
全部这些还能够合并嵌套使用:
'User is of type '+(${user.isAdmin()}?'Administrator':(${user.type}?:'Unknown'))
咱们已经知道,消息表达式许可咱们将:
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
链接到资源,将内容转换为:
home.welcome=欢迎您光临本店!
可是,若是文本内容不是彻底静态的怎么办?例如,咱们想实如今已知某位用访问本站的时候(登陆后),在页面显示他的名字:
<p>张三,您好!欢迎您光临本店!</p>
这意味着咱们须要一个参数:
home.welcome={0},您好!欢迎您光临本店!
参数是根据java.text.MessageFormat标准语法指定,意味着你能够添加数字,日期等api指定的格式。
为了给参数赋值,能够给一个session的属性为用户:
<p th:utext="#{home.welcome(${session.user.name})}"> 张三,您好!欢迎您光临本店! </p>
若是须要,能够指定多个参数,用逗号分隔,事实上,消息的key自己也能够是一个变量:
<p th:utext="#{${welcomeMsgKey}(${session.user.name})}"> 张三,您好!欢迎您光临本店! </p>
咱们已经知道${...}表达式其实是OGNL表达式在执行context中映射的变量。
更多关于OGNL语法和功能的详细信息,能够阅读http://commons.apache.org/ognl/
在SpringMVC的应用中,OGNL将被SpringEL替代,但两种语法很是类似,经常使用的语法彻底相同。
经过OGNL定义,咱们能够指定
<p>当前日期为:: <span th:text="${today}">2016-8-13</span>.</p>
事实上,是这样实现的:
ctx.getVariables("today");
OGNL也容许咱们建立更强大的表达式,好比说这样:
<p th:utext="#{home.welcome(${session.user.name})}"> 张三,您好!欢迎您光临本店! </p>
实际上他是这样执行的:
((User)ctx.getVariables("session").get("user")).getName();
经过getter方法导航是OGNL语法的一大特色:下面演示更多用法:
//访问属性使用(.)进行访问,至关于调用getter方法 ${person.father.name} //访问属性也能够经过方括号[]进行访问,属性名做为一个变量经过单引号(')或双引号(")写在方括号中 ${person['father']['name']} //若是对象是一个map,能够用引号和方括号的语法将至关于直营一个调用它的key获取对象的get方法。 ${countriesByCode.ES} ${personsByName['张三'].age} //对于数组或集合的索引也用方括号来执行 ${personsArray[0].name} //能够调用各类方法,不管是否有参数 ${person.createCompleteName()} ${person.createCompleteNameWithSeparator('-')}
当使用OGNL表达式的context变量的时候,可使用更方便的表达方式。这些对象也是用来#符号:
因此咱们能够这样使用
这里是:<span th:text="${#locale.country}">中国</span>.
你能够在附录1中查看这些对象的所有参考
除了基本对象,Thymeleaf还为咱们提供了一套实用对象,能够帮助咱们在执行表达式中解决一下常见的任务:
#ids:用于处理可能重复的标识属性的使用方法,例如,做为迭代的变量。
你能够在附录2中查看这些工具对象的所有参考
如今,咱们知道了这些工具对象表达式,就能够改变首页中显示日期的方式,首先,改变咱们HomeController中的代码,将:
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd"); Calendar cal=Calendar.getInstance(); ctx.setVariable("today",sdf.format(cal.getTime()));
这三行能够合并为1行
ctx.setVariable("today",Calendar.getInstance());
而后,修改模板文件的相应行为:
<p> 当前日期为: <span th:text="${#calendars.format(today,'yyyy-MM-dd')}">2016年9月5日</span> </p>
他们有个最重要的区别:*{...}表达式的值是在选定的对象而不是整个context的map。也就是说,若是没有选定的对象,*{...}和${...}没有区别
那么问题来了:什么是选定对象?一个th:object对象属性,使用用户权限页面来演示一下:
<div th:object="${session.user}"> <p>姓名:<span th:text="*{firstName}">张三</span></p> <p>年龄:<span th:text="*{age}">26</span></p> <p>国籍:<span th:text="*{nationlity}">中国</span></p> </div>
这至关于:
<div> <p>姓名:<span th:text="${session.user.firstName}">张三</span></p> <p>年龄:<span th:text="${session.user.age}">26</span></p> <p>国籍:<span th:text="${session.user.nationlity}">中国</span></p> </div>
固然,也能够混合使用
<div th:object="${session.user}"> <p>姓名:<span th:text="*{firstName}">张三</span></p> <p>年龄:<span th:text="${session.user.age}">26</span></p> <p>国籍:<span th:text="*{nationlity}">中国</span></p> </div>
当一个使用选定对象的地方,选定的对象其实就是使用了#object表达式的${...}表达式
<div th:object="${session.user}"> <p>姓名:<span th:text="${#object.firstName}">张三</span></p> <p>年龄:<span th:text="${session.user.age}">26</span></p> <p>国籍:<span th:text="${#object.nationlity}">中国</span></p> </div>
也就是说,若是没有已经完成的选定对象,那么,*{...}和${...}两种表达式是彻底等价的。
<div> <p>姓名:<span th:text="*{session.user.firstName}">张三</span></p> <p>年龄:<span th:text="*{session.user.age}">26</span></p> <p>国籍:<span th:text="*{session.user.nationlity}">中国</span></p> </div>
因为其重要性,URL是web应用程序模板的一等公民,Thymeleaf的标准方言都为它定义了特殊语法:@{...}
他有不一样类型的网址:
真正将这些表达式转换并输出为URL的工具,是一个注册在ITemplateEngine中的一个org.thymeleaf.linkbuilder.ILinkBuilder接口的实现类。
Thymeleaf能够在任何状况下处理绝对地址,但相应的,也会要求你给予一个实现了IWebContext接口的context对象,他包含了一些建立连接须要的来自Http请求的相关信息。
在默认状况下,注册的实现是类org.thymeleaf.linkbuilder.StandardLinkBuilder,它对于基于Servlet API的网络或离线应用已经足够了,而其余状况(如非ServletAPI的web项目)则可能须要本身建立接口的实现。
举例说明一下,需使用th:href属性:
<!-- 输出: 'http://localhost:8080/gtvg/order/details?orderId=3' --> <a href="details.html" th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a> <!-- 输出: '/gtvg/order/details?orderId=3' --> <a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a> <!-- 输出: '/gtvg/order/3/details' --> <a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>
注意:
就像#{...}同样,URL表达式中的值也能够是另外一个表达式的结果:
<a th:href="@{${url}(orderId=${o.id})}">view</a> <a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>
如今已经知道了如何建立一个链接,是时候给首页增长一个小菜单了,以告诉你们这个站点还有些什么内容。
<p>请选择:</p> <ol> <li><a href="product/list.html" th:href="@{/product/list}">产品列表</a></li> <li><a href="order/list.html" th:href="@{/order/list}">订单列表</a></li> <li><a href="subscribe.html" th:href="@{/subscribe}">订阅新闻</a></li> <li><a href="userprofile.html" th:href="@{/userprofile}">用户权限</a></li> </ol>
还有一个附加的语法,可用于指向一个服务器的根地址(而不是上下午的根地址),以便于指向同一服务器中的不一样上下文。他的语法为:@{~/path/to/something}
片断表达式是用一种简单的方式来标识一个片断的标记,并能够将他移动的其他模板,它容许咱们执行重写模板,传递给其余模板参数等操做。
作经常使用的片断使用方式是插入使用,属性值为th:insert或th:replace(更多内容见后边部分)
<div th:insert="~{commons :: main}">...</div>
它能够在任何地方使用,就像其余变量同样:
<div th:with="frag=~{footer :: #main/text()}"> <p th:insert="${frag}"> </div>
本教程稍后的部分有一篇完整的关于模板布局的介绍,其中包含变量表达式更深层次的介绍。
字面值指的是包含在单引号之间字符,它可使任何字符,但应该尽可能避免使用\'
如今你看到的是模板文件.
顾名思义,数值显示的就是一个数字:
<p>今年是<span th:text="2016">1942</span>.</p> <p>两年后,将是<span th:text="2013 + 2">1944</span>.</p>
布尔值只有true和false两个值,举例:
<div th:if="${user.isAdmin()} == false"> ...
注意,在这个例子中,==false是写在了\({...}的外边,因此使Thymeleaf自己在支持它,若是写在了\){...}的里边,则变为由OGNL或SpringEL库来支持它。
<div th:if="${user.isAdmin() == false}"> ...
null值能够这样使用
<div th:if="${variable.something} == null"> ...
实际上,数值,布尔值,null值都是一种特殊的token值。
这些token值容许为标准表达式进行一点点的简化,他们的工做方式和文本值彻底同样,可是只能使用字符(a-z,A-Z),数值(0-9),括号即[ ]和( ),
因此不能有空格,括号等。
而且,他能够不被包含在单引号中:
<div th:class="content">...</div>
而不是这样(这样为文本值):
<div th:class="'content'">...</div>
一段文本,不管是一段字面值,变量的返回值,仍是信息表达式,均可以使用+来追加一段文本
<div th:text="'这我的的名字为: ' + ${user.name}">
还能够直接替换一段字面值中包含的字符串信息,而无需经过操做符+
这些替换必须被竖线(|)包围,如:
<span th:text="|欢迎光临这个应用, ${user.name}!|">
即至关于:
<span th:text="'欢迎光临这个应用, ' + ${user.name} + '!'">
格式替换也能够和其余表达式相结合使用:
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
注意:只有变量表达式${...}能够出如今|...|中,其余的如布尔,数值或文字的token,条件表达式等都不可使用。
在表达式中还可使用一些算数运算,如:+,-,*,/,%
<div th:with="isEven=(${prodStat.count} % 2 == 0)" >
注意:同布尔值的例子同样,这个一样能够写成使用OGNL或SpringEL支持的方式:
<div th:with="isEven=${prodStat.count % 2 == 0}">
注意:有两个运算符存在别名:div(/),mod(%)
表达式中的值能够经过>,<,>=,<=进行比较,也能够经过==和!=操做符进行相等的判断。注意在xml中使用的时候,不该该直接使用<,>,应当使用<
和>来代替
<div th:if="${prodStat.count} > 1"> <div th:text="'执行模式为 ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">
注意,这些都存在着别名:gt(>),lt(<),ge(>=),le(<=),not(!),eq(==),neq/ne(!=)
条件表达式为根据条件(另外一个表达式)来选择两个表达式中的一个。
看一个示例:
<tr th:class="${row.even}? 'even' : 'odd'"> ... </tr>
条件表达式的全部的三个部分均在一个自表达式中,意味着他能够用在变量${...},*{...}
,信息#{...}
,URL@{...}
或字面量('...')中.
他还可使用()来进行嵌套
<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'"> ... </tr>
else部分能够省略,若是条件为否,则为一个null值
<tr th:class="${row.even}? 'alt'"> ... </tr>
默认表达式是一种特殊的没有then部分的条件表达式。在一些语言,如Groovy中,它至关于Elvis运算符,它容许有两个表达式,第二个只有在第一个返回null的时候才执行。
修改一下用户权限页,如:
<div th:object="${session.user}"> ... <p>年龄: <span th:text="*{age}?: '(没有输入)'">27</span>.</p> </div>
正如看到的那样,使用?:操做符,咱们在指定当年龄无效的时候,给定一个默认值(使用字面值),这条代码至关于:
<p>年龄: <span th:text="*{age != null}? *{age} : '(没有输入)'">27</span>.</p>
和条件值同样,他也可使用括号来实现嵌套:
<p> 姓名: <span th:text="*{name}?: (*{admin}? 'Admin' : #{default.username})">张三</span> </p>
无操做标记是使用下划线表示(_)
这背后是想指定一个标记来表达指望的结果是什么也不作,也就是说,若是处理的属性(如th:text)没有值。
这将容许开发人员使用原型文本做为默认值,如:
<span th:text="${user.name} ?: 'no user authenticated'">...</span>
将能够改成:
<span th:text="${user.name} ?: _">no user authenticated</span>
直接使用原型,可使使代码更加灵活
Thymeleaf可使用双大括号的方式,为变量${...}和选择*{...}表达式经过配置转换服务进行数据转换。
它基本上是这样的:
<td th:text="${{user.createTime}}">...</td>
这个双大括号${{}}通知Thymeleaf对user.createTime表达式的结果进行转换服务,并在输出结果以前进行格式化操做。
转换服务器默认使用IStandardConversionService的实现类(StandardConversionService类),它只可以简单的执行一个对象的toString(),即将一个对象转为字符串服务,有关注册一个转换服务的更多信息,能够查看稍后更多配置部分。
在thymeleaf-spring3和thymeleaf-spring4有一整套基于Spring转换服务的Thymeelaf转换服务配置,所以他能够自动实现${{}}和*{{}}的服务。
Thymeleaf表达式除了这些特征,还为咱们提供了预处理的功能。
预处理是指在正常的完成一个表达式以前执行的工做。他容许对最终执行的实际表达式进行修改.
一个预处理表达式和一个正常表达式几乎同样,但他在双下划线之间(__$(表达式)__)
好比,国际化的配置文件message_jp.properties的一个条目包含了一个OGNL表达式用于调用一个静态方法:
article.text=@myapp.translator.Translator@translateToJapaese({0})
以及另外一个等效的Message_en.properties:
article.text=@myapp.translator.Translator@translateToEnglish({0})
对此,能够先建立一个用于标记的表达式,这个表达式的值取决于区域设置,为此,使用预处理,而后让Thymeleaf去执行它:
<p th:text="${__#{article.text('textVar')}__}">一些文字...</p>
例如日本,上边预处理与以下内容等效:
<p th:text="${@myapp.translator.Translator@translateToJapaese(textVar)}">Some text here...</p>
预处理中若有字符串__可使用\_\_来进行转义。
本章将介绍如何设置或修改标签属性的值,下一章将讲解如何设置内容的值。
咱们的网站会不按期发布一些新闻,咱们但愿咱们的用户可以订阅他,因此咱们建立一个/WEB-INF/templates/subscribe.html的表单模板:
<form action="subscribe.html"> <fieldset> <input type="text" name="email" /> <input type="submit" value="订阅" /> </fieldset> </form>
这个看起来不错,可是,这个更像是一个普通的html页面,首先,这个表单的action是指向了模板文件自己,并无重写URL,第二,提交按钮的值,他是一个普通的文本,而咱们但愿他是一个国际化的。
使用th:attr属性,它具备设置或修改一个标签属性值的能力。
<form action="subscribe.html" th:attr="action=@{/subscribe}"> <fieldset> <input type="text" name="email" /> <input type="submit" value="订阅!" th:attr="value=#{subscribe.submit}"/> </fieldset> </form>
用法很简单:th:attr将是一个值对应一个属性的表达式,在转换处理后,将会返回以下结果:
<form action="/gtvg/subscribe"> <fieldset> <input type="text" name="email" /> <input type="submit" value="subscribe me!"/> </fieldset> </form>
除了更新了属性值,还能够看到,应用的已经自动将url更新为context前缀的url。
若是,咱们想在同时更新多个属性呢?xml的规则不容许在一个标签内设置两个同名的属性,因此,能够用逗号来分割th:attr的值,好比:
<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
将转换为:
<img src="/gtgv/images/gtvglogo.png" title="这里是logo" alt="这里是logo" />
到了如今,你可能会发现,像:
<input type="submit" value="订阅" th:attr="value=#{subscribe.submit}"/>
这种,看起来是一个很是难看的模板。这种指定属性值的赋值方式很明显不是最优雅的一种建立方式模板。
事实也的确如此,这也就是为何th:attr不多出如今实际应用的模板中,实际中常用的是th:*来实现标签中特定的属性。
在Thymeleaf的标准方言中,一般都是很直观的方式,如设置一个按钮的value值,使用th:value表达式,如:
<input type="submit" value="订阅" th:value="#{subscribe.submit}"/>
看起来感受好多了,而后在看一下表单中的action属性:
<form action="subscribe.html" th:action="@{/subscribe}">
在以前home.html模板中的th:href,其实就在使用的这个语法:
<li><a href="product/list.html" th:href="@{/product/list}">产品列表</a></li>
Thymeleaf针对每个属性都有一个特定的设置语法:
th:abbr | th:accept | th:accept-charset | th:accesskey |
th:action | th:align | th:alt | th:archive |
th:audio | th:autocomplete | th:axis | th:background |
th:bgcolor | th:border | th:cellpadding | th:cellspacing |
th:challenge | th:charset | th:cite | th:class |
th:classid | th:codebase | th:codetype | th:cols |
th:colspan | th:compact | th:content | th:contenteditable |
th:contextmenu | th:data | th:datetime | th:dir |
th:draggable | th:dropzone | th:enctype | th:for |
th:form | th:formaction | th:formenctype | th:formmethod |
th:formtarget | th:fragment | th:frame | th:frameborder |
th:headers | th:height | th:high | th:href |
th:hreflang | th:hspace | th:http-equiv | th:icon |
th:id | th:inline | th:keytype | th:kind |
th:label | th:lang | th:list | th:longdesc |
th:low | th:manifest | th:marginheight | th:marginwidth |
th:max | th:maxlength | th:media | th:method |
th:min | th:name | th:onabort | th:onafterprint |
th:onbeforeprint | th:onbeforeunload | th:onblur | th:oncanplay |
th:oncanplaythrough | th:onchange | th:onclick | th:oncontextmenu |
th:ondblclick | th:ondrag | th:ondragend | th:ondragenter |
th:ondragleave | th:ondragover | th:ondragstart | th:ondrop |
th:ondurationchange | th:onemptied | th:onended | th:onerror |
th:onfocus | th:onformchange | th:onforminput | th:onhashchange |
th:oninput | th:oninvalid | th:onkeydown | th:onkeypress |
th:onkeyup | th:onload | th:onloadeddata | th:onloadedmetadata |
th:onloadstart | th:onmessage | th:onmousedown | th:onmousemove |
th:onmouseout | th:onmouseover | th:onmouseup | th:onmousewheel |
th:onoffline | th:ononline | th:onpause | th:onplay |
th:onplaying | th:onpopstate | th:onprogress | th:onratechange |
th:onreadystatechange | th:onredo | th:onreset | th:onresize |
th:onscroll | th:onseeked | th:onseeking | th:onselect |
th:onshow | th:onstalled | th:onstorage | th:onsubmit |
th:onsuspend | th:ontimeupdate | th:onundo | th:onupload |
th:onvolumechange | th:onwaiting | th:optimum | th:pattern |
th:placeholder | th:poster | th:preload | th:radiogroup |
th:rel | th:rev | th:rows | th:rowspan |
th:rules | th:sandbox | th:scheme | th:scope |
th:scrolling | th:size | th:sizes | th:span |
th:spellcheck | th:src | th:srclang | th:standby |
th:start | th:step | th:style | th:summary |
th:tabindex | th:target | th:title | th:type |
th:usemap | th:value | th:valuetype | th:vspace |
th:width | th:wrap | th:xmlbase | th:xmllang |
th:xmlspace |
还有两个比较特殊的属性即th:alt-title和
th:lang-xmllang,他们能同时设置两个属性:
否则修改首页中刚刚的logo图标签:
<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
修改成:
<img src="../../images/gtvglogo.png" th:src="@{/images/gtvglogo.png}" th:title="#{logo}" th:alt="#{logo}" />
或者:
<img src="../../images/gtvglogo.png" th:src="@{/images/gtvglogo.png}" th:alt-title="#{logo}" />
Thymeleaf还提供了th:attrappend和th:attrprepend属性,用于为属性以前或以后增长值
好比,你可能想在一个按钮的现有css类的基础上在新增一个css类,将会很是容易:
<input type="button" value="点击" class="btn" th:attrappend="class=${' ' + cssStyle}" />
若是你对cssStyle变量的值为warning,那么输出将为:
<input type="button" value="点击" class="btn warning" />
由于样式表使用的如此频繁,因此标准方言中还有两个附加属性,th:classappend和th:styleappend属性,用于追加一个class类或者一段样式表而不改变现有内容:
<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">
each为迭代属性,稍后介绍。
在XHTML/HTML5中有些属性是特殊的,它们要么是一个固定值,要么根本就不存在,好比:
<input type="checkbox" name="option1" checked="checked" /> <input type="checkbox" name="option2" />
在严格模式下,checked不能有其余的值,相似的还有disabled,muliple,readonly,selected等。
在标准方言中,容许你经过一个条件值来设置这些属性,若是值为真,这些属性将设置为它的固定值,若是为假,则不会设置此属性。如:
<input type="checkbox" name="active" th:checked="${user.active}" />
在标准方言中,相似的属性以下:
th:async | th:autofocus | th:autoplay | th:checked |
th:controls | th:declare | th:default | th:defer |
th:disabled | th:formnovalidate | th:hidden | th:ismap |
th:loop | th:multiple | th:novalidate | th:nowrap |
th:open | th:pubdate | th:readonly | th:required |
th:reversed | th:scoped | th:seamless | th:selected |
除了刚刚看到的对于特定属性的处理外,Thymeleaf还在标准方言中提供了一个默认属性处理器,能够设置任意自定义属性的值,甚至能够没有具体的th:*处理器。
好比:
<span th:whatever="${user.name}">...</span>
将返回
<span whatever="张三">...</span>
还可使用一些彻底不一样的语法来应用到模板之中,它对html5更加友好。
<table> <tr data-th-each="user : ${users}"> <td data-th-text="${user.login}">...</td> <td data-th-text="${user.name}">...</td> </tr> </table>
例子中 data-{前缀}-{名字}这种语法是一个html5的自定义属性的标准方式。这种方式下,开发者无需导入命名空间,如th:,Thymeleaf将自动提供执行(全部方言模式,不只是标准方言)。
还有一种语法来指定自定义标签:{前缀}-{名字},这里遵循的是W3C标准的自定义标签。这也是可使用,举个例子,好比th:block就可使用th-block,这个将在稍后解释。
注意:这个语法是th:*命名空间的一个补充,并非他的替换,在为了跟本没有弃用命名空间语法的想法。
到目前为止,这个网络商店已经有了一个主页,有了一个用户配置的页面,还有了一个让用户订阅咱们的通信页,可是,咱们的产品呢?咱们是否是首先应该有一个产品列表,让客户知道咱们在卖什么东东么?嗯,显然是的,让咱们如今就走吧!
在模板/WEB-INF/templates/product/list.html中要显示产品列表,须要一个table。而后每一个产品在显示上是table的一行,因此在咱们的模板中,就须要Thymeleaf来迭代每个产品。
在标准方言中,提供了一个迭代属性,为th:each
对于产品列表页,首先须要更改产品列表的控制器,让其从服务层中获取产品数据,然添加到模板的context中。
public void process(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, ITemplateEngine templateEngine) throws Exception { ProductService productService=new ProductService(); List<Product> allProducts=productService.findAll(); WebContext ctx=new WebContext(request,response,servletContext,request.getLocale()); ctx.setVariable("prods", allProducts); templateEngine.process("product/list", ctx,response.getWriter()); }
而后,在模板中经过th:each来迭代产品:
<h1>产品列表</h1> <table> <tr> <th>产品名称</th> <th>产品价格</th> <th>有现货</th> </tr> <tr th:each="prod:${prods}"> <td th:text="${prod.name}">土豆</td> <td th:text="${prod.price}">2.41</td> <td th:text="${prod.inStock}?#{true}:#{false}">yes</td> </tr> </table> <p> <a href="../home.html" th:href="@{/}">返回首页</a> </p>
**prod:\({prods}**属性值的意思是,迭代\){prods}的每一个元素并重复这个模板的这个片断。而后解释一下这两部分分别的意思:
注意:迭代值止只能够用在tr节点上面(包括迭代里边包含的td标签)。
不是只有java.util.List对象可使用Thymeleaf进行迭代。事实上,任何一个完整的对象集均可以使用th:each属性:
当使用th:each的时候,Thymeleaf会提供一个跟着迭代状态的机制:状态变量。
状态定义被封装在th:each的属性中。并包含如下数据:
如今修改一下前一个例子:
<h1>产品列表</h1> <table> <tr> <th>产品名称</th> <th>产品价格</th> <th>有现货</th> </tr> <tr th:each="prod,iterStat:${prods}" th:class="${iterStat.odd}?'odd'"> <td th:text="${prod.name}">土豆</td> <td th:text="${prod.price}">2.41</td> <td th:text="${prod.inStock}?#{true}:#{false}">yes</td> </tr> </table> <p> <a href="../home.html" th:href="@{/}">返回首页</a> </p>
能够看到,状态变量(即iterStat)的定义:将这个变量的名字做为属性写在迭代值以后,用逗号于迭代值隔开。产生了迭代值以后,他的状态值就能够也仅仅能够在th:each包含的代码段中使用。
这段代码的执行结果为:
能够看到,这端代码执行的很是完美。而且只在奇数行添加了odd的css(行数从0开始)
若是没有显示的设置一个状态变量,Thymeleaf会默认的建立一个使用迭代值+Stat后缀的迭代变量,如:
<table> <tr> <th>产品名称</th> <th>产品价格</th> <th>有现货</th> </tr> <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> </table>
有时咱们须要对只有检索操做的数据集的检索(如从数据库查询)进行优化。
事实上,他能够应用于任何的数据块,而检索内存中可能要重复使用的集合多是最多见的状况。
为了可以支持这一点,Thymeleaf提供了懒加载上下文变量的机制,上下文的变量内容经过时实现ILazyContextVariable接口(最有可可能使用它默认实现:LazyContextVariable),它将在模板解析的时候来执行,如:
context.setVariable( "users", new LazyContextVariable<List<User>>() { @Override protected List<User> loadValue() { return databaseRepository.findAllUsers(); } });
这个变量就可使用懒加载,,使用方式为:
<ul> <li th:each="u : ${users}" th:text="${u.name}">user name</li> </ul>
但若是为下边的代码,条件为false的时候也将不会被初始化(它的loadValue()方法将不会被调用):
<ul th:if="${condition}"> <li th:each="u : ${users}" th:text="${u.name}">user name</li> </ul>
有时你可能想要,模板的某个片断只有在条件被知足的时候才出如今结果中。
好比,假设咱们要在产品表中显示一个列,该列针对每一个产品的评论,若是有评论,则连接到评论页面。
要实现这个功能,就要用到前面说到过的th:if属性:
<table> <tr> <th>产品名称</th> <th>产品价格</th> <th>有现货</th> <th>用户评价</th> </tr> <tr th:each="prod:${prods}"> <td th:text="${prod.name}">土豆</td> <td th:text="${#numbers.formatDecimal(prod.price,0,2)}">2.41</td> <td th:text="${prod.isStock}?#{true}:#{false}">yes</td> <td> <span th:text=${#lists.size(prod.comments)}>2</span>个评价 <a href="comments.html" th:href="@{/product/comments(prodId=${prod.id})}" th:if="${not #lists.isEmpty(prod.comments)}">查看</a> </td> </tr> </table>
重点注意下面这一行:
<a href="comments.html" th:href="@{/product/comments(prodId=${prod.id})}" th:if="${not #lists.isEmpty(prod.comments)}">查看</a>
事实上,这段代码没什么好解释的,若是产品有评论,那么咱们就建立一个跳转到评论页面的超连接,而且使用产品ID做为参数。
查看生成结果:
Perfect!这正是咱们想要的!
th:if不光可使用布尔值,一下规则均可以:
th:if还有一个互逆的表达式为th:unless,还继续用以前的例子做一个演示:
<a href="comments.html" th:href="@{/comments(prodId=${prod.id})}" th:unless="${#lists.isEmpty(prod.comments)}">查看</a>
条件选择还能够像java同样,使用switch来声明:在Thymeleaf中使用th:switch和th:case来实现。
他的工做方式会和你想的同样:
<div th:switch="${user.role}"> <p th:case="'admin'">超级管理员用户</p> <p th:case="#{roles.manager}">管理员用户</p> </div>
注意:一旦一个th:case被判断为真,那么其余的同等级的th:case都将被判断为假
default的写法为th:case="*"
<div th:switch="${user.role}"> <p th:case="'admin'">超级管理员用户</p> <p th:case="#{roles.manager}">管理员用户</p> <p th:case="*">其余用户</p> </div>
咱们常常会想让咱们的模板包含一些其余模板,比较常见的用途如页眉,页脚,菜单等。
为了作到这一点,Thymeleaf须要咱们定义一些可用片断,咱们能经过th:fragment属性来实现这一点。
如今,咱们要添加一个带标准版权声明的页脚文件(/WEB-INF/templates/footer.html):
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <body> <div th:fragment="copy"> © 网络商店 </div> </body> </html>
上面定义了一个叫copy的代码片断,咱们能经过th:insert和th:replace属性(还有th:include,但Thymeleaf3.*版本不推荐使用),很容易的将他导入到首页中:
<body> ... <div th:insert="~{footer :: copy}"></div> </body>
注意一点,th:insert使用的是一个片断表达式(~{...}),或片断中一个更具体的表达式。但在前者的状况下(简单片断表达式),好比就像上边的代码,~{...}是可选的,因此以下班的代码是等价的:
<body> ... <div th:insert="footer :: copy"></div> </body>
判断表达式语法很是简单,有三种不一样的格式:
dom选择器语法相似于XPath或css选择器,更多内容见附录C。
注意:此时由th:insert和th:replace标签导入的模板必须在当前的模板引擎下的模板解释器能够分辨。
在上面的格式中,模板名和dom选择器均可以使用任何表达式的结果来表示,好比:
<div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>
片断中能够包含任何th:*属性。一点判断被包含到目标模板(即便用th:insert/th:replace的文件)中,这些属性将被执行。而后他们将能使用目标模板中的任何context变量。
这种方法有个很大的优点:你的任何片断的代码,完整的代码都可以显示在浏览器中,同时,仍保留了使用Thymeleaf把他导入到其余模板中的能力。
此外,因为强大的dom选择器,使咱们能够导入不含有th:fragment属性的代码片断,他甚至能够标记全部来自Thymeleaf所不知道的应用,如:
<div id="copy-section"> © 2011 网络商店 </div>
这里能够就像css同样的使用它的id属性:
<body> ... <div th:insert="footer :: #copy-section"></div> </body>
好了,如今让咱们看看这几个属性有什么区别(th:insert,th:replace和th:include(3.*版本不推荐使用)):
不够直观?举个例子:
<div th:fragment="copy"> © 网络商店 </div>
导入到两个div标签中:
<body> ... <div th:insert="footer :: copy"></div> <div th:replace="footer :: copy"></div> <div th:include="footer :: copy"></div> </body>
执行结果:
<body> ... <div> <footer> © 网络商店 </footer> </div> <footer> © 网络商店 </footer> <div> © 网络商店 </div> </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:replace="::frag (twovar=${value2},onevar=${value1})">...</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}">
注意,在这里,不管参数是否有签名,都定义的是一个片断的局部变量,因此不会致使它覆盖或清空以前的context变量,片断仍然能够正常访问调用它的模板的每一个context变量。
th:assert属性能够定义一个用逗号分隔的表达式,用来为每个条件作出评估,以判断是否产生异常。
<div th:assert="${onevar},(${twovar} != 43)">...</div>
这是一个在片断签名时就验证参数的方便方式:
<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>
基于片断表达式,咱们能够为片断指定一个非文本,数字,javabean的参数,以代替标记。
这样咱们就能使用这样一种方式,它可使用丰富的标记来调用模板,以实现一个很是灵活的模板布局机制。
注意title和links变量在片断中的使用:
<head th:fragment="common_header(title,links)"> <title th:replace="${title}">这个超帅应用</title> <!-- 经常使用样式脚本 --> <link rel="stylesheet" type="text/css" media="all" th:href="@{/css/myapp.css}"> <link rel="shortcut icon" th:href="@{/images/favicon.ico}"> <script type="text/javascript" th:src="@{/sh/scripts/codebase.js}"></script> <!--/* 每页占位符连接 */--> <th:block th:replace="${links}" /> </head>
咱们使用的片断
... <head th:replace="base :: common_header(~{::title},~{::link})"> <title>超帅应用 - Main</title> <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}"> <link rel="stylesheet" th:href="@{/themes/smoothness/jquery-ui.css}"> </head> ...
结果将咱们的调用的模板的实际的
http://www.cnblogs.com/jiangchao226/p/5900222.html