Scala 模板语法

基于Scala的类型安全的模板引擎

Play 自带Twirl,这是一种强大的基于Scala的模板引擎, 它的设计是受到了ASP.NET Razor的启发。具体来讲:

  • 简洁, 富于表现力, 和流体: 它最小化了文件中需要的字符数和按键数, 以及快速、流体编码的工作流。不像多数模板语法, 你无须在HTML中显式标注服务器代码块而打断代码,解析器可以聪明地从代码中自动推断。这使得模板语法简洁紧凑而富有表现力,编码干净,快速而且有趣。
  • 容易学习: 它只需学习少量概念,就让你快速提高生产力。只需要简单的Scala构念和所有你已有的HTML技能。
  • 不是一门新语言: 我们有意识地选择不去创建一门新语言,而是想让Scala开发者使用他们已有的Scala语言技能, 并提供一种有很棒的HTML构建工作流的模板语法。
  • 可在任意文本编辑器中编辑: 它不需要特殊的工具,你可以用老旧的纯文本编辑器高效使用它。

模板会被编译, 因此如果有错误,你可以在浏览器中看到:

""

概述

Play Scala模板是一个包含小块Scala代码的简单文本文件。模板可以生成任何基于文本的格式, 如HTML, XML或CSV。

模板系统的设计使HTML使用者用起来很舒服, 前端开发者很容易用模板工作。

模板会编译为标准的Scala函数, 以简单的命名约定。如果你创建一个views/Application/index.scala.html 模板文件,它会生成一个有apply() 方法的views.html.Application.index 类。

举例,这里有一个简单的模板:

@(customer: Customer, orders: List[Order])

<h1>Welcome @customer.name!</h1>

<ul>
@for(order <- orders) {
  <li>@order.title</li>
}
</ul>

你可以在任意Scala代码块中调用上述代码,就像平时调用一个类的方法一样:

val content = views.html.Application.index(c, o)

语法: 有魔法的 ‘@’ 字符

Scala模板只使用单个@ 作为特殊字符。每次遇到这个字符, 就表明动态语句的开始。你无须显式关闭代码块 - 动态语句的结束位置会从代码自动推断:

Hello @customer.name!
       ^^^^^^^^^^^^^
       Dynamic code

因为模板引擎通过分析你的代码而自动检测代码块的结束, 所以语法只支持简单的语句。如果你想要插入复杂的语句, 要用括号显式是标注它:

Hello @(customer.firstName + customer.lastName)!
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    Dynamic Code

你也可以使用花括号, 来同时写多条语句到一个块中:

Hello @{val name = customer.firstName + customer.lastName; name}!
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                             Dynamic Code

因为 @ 是一个特殊字符, 你会有时候需要转义它,通过用 @@:

My email is bob@@example.com

模板参数

一个模板就像一个函数, 因此它也需要参数, 其必须在模板文件的顶部声明:

@(customer: Customer, orders: List[Order])

你也可以为参数使用默认值:

@(title: String = "Home")

甚至可以提供几个参数组:

@(title: String)(body: Html)

遍历

你可以使用for 关键词, 以一个漂亮的标准方式进行遍历:

<ul>
@for(p <- products) {
  <li>@p.name ($@p.price)</li>
}
</ul>

注意: 确保{for在同一行,用于指明表达式还没有结束,会在下一行继续。

If 块

If块没有什么特别的。简单使用Scala的标准if 语句即可:

@if(items.isEmpty) {
  <h1>Nothing to display</h1>
} else {
  <h1>@items.size items!</h1>
}

声明可重用的块

你可以创建可重用的代码块:

@display(product: Product) = {
  @product.name ([email protected])
}

<ul>
@for(product <- products) {
  @display(product)
}
</ul>

注意你也可以声明可重用的纯代码块:

@title(text: String) = @{
  text.split(' ').map(_.capitalize).mkString(" ")
}

<h1>@title("hello world")</h1>

注意: 声明代码块这种方式有模板中有时候是有用的,但要记住模板中不宜放复杂的逻辑。这部分代码最好放在外部的Scala类中(如果你愿意也可以放在views/ 包下)。

按照惯例,一个可重用块的命名如果以 implicit 开始,那么它会标记为implicit:

@implicitFieldConstructor = @{ MyFieldConstructor() }

声明可重用的值

你可以使用defining 助手方法来定义一个作用域中的值:

@defining(user.firstName + " " + user.lastName) { fullName =>
  <div>Hello @fullName</div>
}
`

导入语句

你可以在模板(或子模板)的开始处导入任何你想要的东西:

@(customer: Customer, orders: List[Order])

@import utils._

...

要用绝对的解析路径,要在导入语句前使用 root 前缀。

@import _root_.company.product.core._

如果你在所有的模板中都要导入一个公共的东西, 可以在build.sbt 中声明:

TwirlKeys.templateImports += "org.abc.backend._"

注释

你可以在模板中使用@* *@ ,来写服务端的块注释:

@*********************
* This is a comment *
*********************@

你可以在模板的首行写上注释,来文档化你的模板到 Scala API doc中:

@*************************************
 * Home page.                        *
 *                                   *
 * @param msg The message to display *
 *************************************@
@(msg: String)

<h1>@msg</h1>

转义

默认情况下, 动态内容部分会根据模板类型(如HTML或XML)的规则进行转义。如果你想要输出原始内容,要将它包装在模板的内容类型中。

例如输出原始 HTML:

<p>
  @Html(article.content)
</p>

String interpolation

模板引擎可以用作 string interpolator。基本上只是将 “@” 换成 “$”:

import play.twirl.api.StringInterpolation

val name = "Martin"
val p = html"<p>Hello $name</p>"