Using the template engine to build web pages

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.

Inserting dynamic values

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:

Inserting dynamic values

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&nbsp;€</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).

Note

To insert the @ character in a template, you need to escape it by typing in @@.

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();
    }
}

Note

If the result of a dynamic expression contains an HTML character entity, it will be escaped by the template engine: @("<") produces &lt;. 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: &nbsp; produces &nbsp; (and not &amp;nbsp;). You can bypass the escaping process by inserting a dynamic value that already has type Html: @Html("<h1>danger</h1>").

Looping and branching

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&nbsp;€ }

Finally, the match expressions are also supported:

@item.price match {
  case 0 => { FREE }
  case p => { @p&nbsp;€ }
}

Reusing document fragments

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&nbsp;€</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&nbsp;€</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&nbsp;€</p>
}
@layout("Item details")(content(item))

Comments

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&nbsp;€</p>
}

Then, if you generate the API documentation of your application using the doc sbt command, your HTML templates are documented!

Import statements

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.9.147