For now, your Play application only handles JSON. To create data, you must supply a JSON payload with your HTTP request, and ensure the presentation of your application resources is only JSON. This can be sufficient if you just want to provide a web service. However, you sometimes also want to expose your resources as HTML pages so that users can browse them from their web browser.
Play includes a template engine, Twirl, that makes it easier to define skeleton HTML pages filled with data from your application and combine document fragments.
The app/views/main.scala.html
file contains the HTML template used by the provided controllers.Application.index
action. Have a look at it in your code editor. It contains a simple HTML document. The Application.index
action renders it using the views.html.main()
method (or views.html.main.render()
in Java). You might ask how is the app/views/main.scala.html
file related to the views.html.main
object? The Play sbt plugin automatically generates an object corresponding to each file located under the app/views/
directory, whose extension is .scala.html
. This object is named according to the filename. Each app/views/<pkg>/<name>.scala.html
file produces an object whose fully qualified name is views.html.<pkg>.<name>
. Here, <pkg>
is an arbitrary succession of subdirectories (which may be empty, as in the provided template). The syntax of templates is explained in the upcoming sections.
Let's create an HTML template to show the details of an item. For example, for an item named Play Framework Essentials that has a price of 42, the template should render the following page:
Copy the content of the main.scala.html
file to a new file named details.scala.html
and edit its content to look like the following:
@(item: models.Item) <!DOCTYPE html> <html> <head> <title>Item details</title> </head> <body> <h1>Item details</h1> <p><strong>@item.name</strong>: @item.price €</p> </body> </html>
The first line says that this template takes one parameter of type models.Item
named item
. Then, @item.name
and @item.price
insert the item name and price, respectively, in the template.
A template is a text document that contains dynamic expressions prefixed by the special character @
. The content of the expression that is after the @
character is inserted when the template is rendered. For a complex expression, you can use parentheses, for example, @(1 + 1)
. However, if your expression is just an access to a value member (like in your current details.scala.html
template), you can omit the parentheses. The expression language of dynamic expressions is Scala (even for Java applications).
The generated object corresponding to this template is similar to a Scala function taking one Item
parameter and returning an Html
value. The Html
type is defined by Play and is supported by actions so that supplying a value of the Html
type as an HTTP response body automatically sets its content type to text/html
.
You can call it from your Items.details
action as follows:
def details(id: Long) = Action {
shop.get(id) match {
case Some(item) => Ok(views.html.details(item))
case None => NotFound
}
}
The Java equivalent code is as follows:
public static Result details(Long id) {
Item item = shop.get(id);
if (item != null) {
return ok(views.html.details.render(item));
} else {
return notFound();
}
}
If the result of a dynamic expression contains an HTML character entity, it will be escaped by the template engine: @("<")
produces <
;. This behavior helps prevent cross-site scripting if a dynamic expression refers to some user submitted data. However, such characters are not escaped in the static parts of the template:
produces
(and not &nbsp;
). You can bypass the escaping process by inserting a dynamic value that already has type Html
: @Html("<h1>danger</h1>")
.
The template engine supports special statements for branching and looping. For instance, a template showing the list of items can be defined as follows:
@(items: Seq[models.Item]) <!DOCTYPE html> <html> <head> <title>Items list</title> </head> <body> <h1>Items list</h1> <ul> @for(item <- items) { <li> <a href="@controllers.routes.Items.details(item.id)"> @item.name </a> </li> } </ul> </body> </html>
In addition to the @for
statement, you can use @if
:
@if(
item.price == 0)
{ FREE } else { @item.price € }
Finally, the match
expressions are also supported:
@item.price match {
case 0 => { FREE }
case p => { @p € }
}
You might have noticed that the template that shows the list of items and the template that shows an item's details are very similar and duplicate a lot of content. Hopefully, as templates are functions, you can compose them just like functions are compose. Let's generalize the details.scala.html
and list.scala.html
templates in a template named layout.scala.html
:
@(title: String)(content: Html)
<!DOCTYPE html>
<html>
<head>
<title>@title</title>
</head>
<body>
<h1>@title</h1>
@content
</body>
</html>
This template takes two parameter lists (of one parameter each), the first one defines the page title and the second one, the page content as an HTML fragment. The details.scala.html
template can then be rewritten as follows:
@(item: models.Item)
@layout("Item details") {
<p><strong>@item.name</strong>: @item.price €</p>
}
This template calls the layout.scala.html
template with "Item details"
as title and the small HTML fragment as content. Note that the use of parentheses or braces is significant: the content between braces is interpreted as an HTML fragment.
Similarly, the list.scala.html
template can be rewritten as follows:
@(items: Seq[models.Item])
@layout("Items list") {
<ul>
@for(item <- items) {
<li>
<a href="@controllers.routes.Items.details(item.id)">@item.name</a>
</li>
}
</ul>
}
Reusing the same layout between templates makes the code easier to maintain.
You can also define reusable values visible only in the scope of a template:
@(item: models.Item) @content = { <p><strong>@item.name</strong>: @item.price €</p> } @layout("Item details")(content)
In the preceding code, content
is a local value that can be used in the rest of the template. Note that local values can also be functions:
@(item: models.Item) @content(item: models.Item) = { <p><strong>@item.name</strong>: @item.price €</p> } @layout("Item details")(content(item))
Comments are supported with the following syntax:
@* this is a comment *@
Comments produce nothing in the template output.
Note that writing a comment immediately before the template parameters declaration produces a Scaladoc comment on the generated object corresponding to the template:
@** * Displays an item * @param item Item to display *@ @(item: models.Item) @layout("Item details") { <p><strong>@item.name</strong>: @item.price €</p> }
Then, if you generate the API documentation of your application using the doc
sbt command, your HTML templates are documented!
Import statements are supported using the following syntax:
@import models.Item @content(item: Item) = { … }
If you have something imported in a lot of templates, you can define a global import in your build.sbt
file:
templatesImport += "models._"
The templatesImport
setting is defined by the Play sbt plugin and used by the template compiler. It adds the corresponding imports on top of each generated object.
3.144.9.147