Thymeleaf 文本
1. 多语言文本
我们的第一个任务是为我们的杂货网站创建一个主页。
这个页面的第一个版本非常简单:只是标题和欢迎信息。这是我们的/WEB-INF/templates/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}">Welcome to our grocery store!</p> </body> </html>
您将注意到的第一件事是该文件是HTML5,任何浏览器都可以正确显示它,因为它不包含任何非HTML标记(浏览器会忽略它们不理解的所有属性,例如th:text)。
但您可能还注意到此模板实际上不是有效的 HTML5文档,因为th:* HTML5规范不允许我们在表单中使用这些非标准属性。事实上,我们甚至xmlns:th在我们的标签中添加了一个属性,这绝对不是HTML5-ish:
<html xmlns:th="http://www.thymeleaf.org">
...在模板处理中根本没有任何影响,但作为一个咒语,阻止我们的IDE抱怨缺少所有这些th:* 属性的命名空间定义。
那么如果我们想让这个模板HTML5有效呢?简单:切换到Thymeleaf的数据属性语法,使用data-属性名称和hyphen(-)分隔符的前缀而不是分号(:):
<!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" data-th-href="@{/css/gtvg.css}" /> </head> <body> <p data-th-text="#{home.welcome}">Welcome to our grocery store!</p> </body> </html>
ata-HTML5规范允许使用自定义前缀属性,因此,使用上面的代码,我们的模板将是有效的HTML5文档。
两种表示法都是完全等效且可互换的,但为了代码示例的简单性和紧凑性,本教程将使用名称空间表示法(th: )。此外,th: 符号更通用,并且在每个Thymeleaf模板模式(XML,TEXT...)中data-都允许使用,而符号仅在HTML模式下允许。
1) 使用th:文本和外化文本
外化文本是从模板文件中提取模板代码的片段,以便它们可以保存在单独的文件(通常是.properties文件)中,并且可以使用其他语言编写的等效文本(称为国际化或简称i18n)轻松替换它们。外化的文本片段通常称为“消息”。
消息总是有一个标识它们的键,而Thymeleaf允许您指定文本应该与具有以下#{...}语法的特定消息相对应:
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
我们在这里看到的实际上是Thymeleaf标准方言的两个不同特征:
- 该th:text属性评估其值表达式并将结果设置为主机标签的主体,有效地替换了我们在代码中看到的“欢迎使用我们的杂货店!”文本。
- #{home.welcome}表达,在指定的标准表达式语法,指示要由所使用的文本th:text属性应与该消息home.welcome对应于哪个语言环境,我们正在处理与模板键。
现在,这个外化文本在哪里?
Thymeleaf中外化文本的位置是完全可配置的,它取决于org.thymeleaf.messageresolver.IMessageResolver所使用的具体实现。通常,.properties将使用基于文件的实现,但是如果我们想要从数据库获取消息,我们可以创建自己的实现。
但是,我们在初始化期间没有为模板引擎指定消息解析器,这意味着我们的应用程序正在使用标准消息解析器,由org.thymeleaf.messageresolver.StandardMessageResolver。实现。
标准消息解析程序希望/WEB-INF/templates/home.html在属性文件中查找相同文件夹中的消息,并使用与模板相同的名称,如:
- /WEB-INF/templates/home_en.properties 用于英文文本。
- /WEB-INF/templates/home_es.properties 西班牙语文本。
- /WEB-INF/templates/home_pt_BR.properties 用于葡萄牙语(巴西)语言文本。
- /WEB-INF/templates/home.properties 对于默认文本(如果区域设置不匹配)。
我们来看看我们的home_es.properties文件:
home.welcome=¡Bienvenido a nuestra tienda de comestibles!
这就是我们将Thymeleaf流程作为模板所需的全部内容。让我们创建我们的Home控制器。
2. Contexts
为了处理我们的模板,我们将创建一个HomeController实现IGTVGController我们之前看到的接口的类:
public class HomeController implements IGTVGController { public void process( final HttpServletRequest request, final HttpServletResponse response, final ServletContext servletContext, final ITemplateEngine templateEngine) throws Exception { WebContext ctx = new WebContext(request, response, servletContext, request.getLocale()); templateEngine.process("home", ctx, response.getWriter()); } }
我们看到的第一件事是创建一个上下文。Thymeleaf上下文是实现org.thymeleaf.context.IContext接口的对象。上下文应包含在变量映射中执行模板引擎所需的所有数据,并且还引用必须用于外部化消息的语言环境。
public interface IContext { public Locale getLocale(); public boolean containsVariable(final String name); public Set<String> getVariableNames(); public Object getVariable(final String name); }
这个接口有一个专门的扩展,org.thymeleaf.context.IWebContext用于基于ServletAPI的Web应用程序(如SpringMVC)。
public interface IWebContext extends IContext { public HttpServletRequest getRequest(); public HttpServletResponse getResponse(); public HttpSession getSession(); public ServletContext getServletContext(); }
Thymeleaf核心库提供了以下每个接口的实现:
org.thymeleaf.context.Context 器物 IContext org.thymeleaf.context.WebContext 器物 IWebContext 正如您在控制器代码中看到的那样,WebContext是我们使用的那个。实际上我们必须这样做,因为使用a ServletContextTemplateResolver要求我们使用上下文实现IWebContext。
- org.thymeleaf.context.Context implements IContext
- org.thymeleaf.context.WebContext implements IWebContext
正如您在控制器代码中看到的那样,WebContext是我们使用的那个。实际上我们必须这样做,因为使用a ServletContextTemplateResolver要求我们使用上下文实现IWebContext。
WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());
这四个构造函数参数中只有三个是必需的,因为如果没有指定系统,将使用系统的默认语言环境(尽管在实际应用程序中不应该发生这种情况)。
我们可以使用一些专门的表达式来从WebContext我们的模板中获取请求参数以及请求,会话和应用程序属性。例如:
- ${x}将返回x存储在Thymeleaf上下文中的变量或作为请求属性。
- ${param.x}将返回一个被调用的请求参数x(可能是多值的)。
- ${session.x}将返回一个名为的会话属性x。
- ${application.x}将返回一个名为的servlet上下文属性x。
3. 执行模板引擎
准备好上下文对象后,现在我们可以告诉模板引擎使用上下文处理模板(通过其名称),并将响应编写器传递给它,以便可以将响应写入它:
templateEngine.process("home", ctx, response.getWriter());
让我们看看使用西班牙语语言环境的结果:
<!DOCTYPE html> <html> <head> <title>Good Thymes Virtual Grocery</title> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/> <link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" /> </head> <body> <p>¡Bienvenido a nuestra tienda de comestibles!</p> </body> </html>
4. 有关文本和变量的更多信息
未转义的文字
我们主页的最简单版本现在似乎已经准备就绪,但是有一些我们没有想过的......如果我们有这样的消息怎么办?
home.welcome=Welcome to our <b>fantastic</b> grocery store!
如果我们像以前一样执行此模板,我们将获得:
<p>Welcome to our <b>fantastic</b> grocery store!</p>
这不完全符合我们的预期,因为我们的标签已被转义,因此它将显示在浏览器中。
这是th:text属性的默认行为。如果我们希望Thymeleaf尊重我们的HTML标签而不是逃避它们,我们将不得不使用不同的属性:( th:utext对于“未转义的文本”):
<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>
这将输出我们的消息,就像我们想要的那样:
<p>Welcome to our <b>fantastic</b> grocery store!</p>
5. 使用和显示变量
现在让我们在主页上添加更多内容。例如,我们可能希望在欢迎消息下方显示日期,如下所示:
Welcome to our fantastic grocery store! Today is: 12 july 2010
首先,我们必须修改控制器,以便将该日期添加为上下文变量:
public void process( final HttpServletRequest request, final HttpServletResponse response, final ServletContext servletContext, final ITemplateEngine templateEngine) throws Exception { SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy"); Calendar cal = Calendar.getInstance(); WebContext ctx = new WebContext(request, response, servletContext, request.getLocale()); ctx.setVariable("today", dateFormat.format(cal.getTime())); templateEngine.process("home", ctx, response.getWriter()); }
我们添加了一个String调用today我们上下文的变量,现在我们可以在模板中显示它:
<body> <p th:utext="#{home.welcome}">Welcome to our grocery store!</p> <p>Today is: <span th:text="${today}">13 February 2011</span></p> </body>
正如您所看到的,我们仍在使用th:text作业的属性(这是正确的,因为我们想要替换标签的主体),但这次语法有点不同而不是#{...}表达式值,我们使用的是${...}一。这是一个变量表达式,它包含一个名为OGNL(对象图形导航语言)的语言表达式,该表达式将在我们之前讨论过的上下文变量映射上执行。
该${today}表达式只是表示“今天拿到称为变量”,但这些表述可能更加复杂(如${user.name}“获取被叫用户的变量,并调用它的getName()方法”)。
属性值有很多可能性:消息,变量表达式......还有很多。下一章将向我们展示所有这些可能性。
下一章:Thymeleaf 表达式语法
我们将在杂货虚拟商店的开发中稍作休息,以了解Thymeleaf标准方言中最重要的部分之一:Thymeleaf标准表达式语法。我们已经看到在这种语法中表达的两种类型的有效属性值:消息和变量表达式:<p t ...