Skip to content

The main template, layout and tags

branaway edited this page Sep 14, 2010 · 9 revisions

We’ll dissect a typical template that uses layout and tags. This example is the index.html page from the Play’s Yabe example, re-written in Japid.

What I call the template is the template that is called directly by the controller. Templates can inherit from a layout, which basically defines the big page structure, such as the header, footer ans side-bar. Tags are unit of template that can be called by another template repeatedly for code reuse. In fact, tags are really regular templates: they can take parameters; they can also extends from other layouts, although they usually don’t use layouts.

The


play japid:mkdir

creates the recommended package structure for Japid:

- app/japidviews/javatags, for your java based tag definitions
- app/japidviews/_layouts, for the layout templates
- app/japidviews/
tags, for tags.

It also creates a package for each controllers in the app/controllers/ directory/subdirectory, such as:

- app/japidviews/Application

Since Japid templates are transformed to Java source code and linked statically to the controller actions, The templates can placed anywhere in the source directory. The above hierarchy is recommended because they’re imported to the templates by default, saving you from explicitly importing them by using the

`import xxx.yyy.zzz.*

in your templates.

The main template

Here is a complete page modeled after the index.html of the Yabe sample project in the Play! distribution.

`contentType 	text/html 
`extends 		main.html
`args 		Post frontPost, List<Post> olderPosts

#{set title}HOME #{/}

`if(frontPost != null) {
    #{display (frontPost, "home") /}  
    `if (olderPosts.size() > 0) {
        <div class="older-posts">    
            <h3>Older posts <span class="from">from the blog</span></h3>
		`for (Post p : olderPosts) { 
			#{display(p, "home")/}
            `}
        </div>
    `}
`} else {
    <div class="empty">
        There is currently nothing to read here.
    </div>
`}

Some interesting lines:

`contentType text/html 

set the response content type. equivalent to `setHeader Content-Type text/html. The setHeader directive can be used set any http response headers.

`extends main.html
use main.html in the japidviews/_layouts/main.html as the “layout” of this template
`args Post frontPost, List<Post> olderPosts

specify the parameters of this template

#{set title}HOME #{/}

set a named block to satisfy a requirement of this named block in the layout, namely #{get title/}

`if(frontPost != null) {
`} else {
`}

anything after the ` sign is treated as plain Java syntax, except for some special directives, such as the extends, args, import etc.

    #{display (frontPost, "home") /}

invoke a tag named display.html in the japdiviews._tags package, with two parameters

The layout

Again, this file is modeled after the Yabe’s main.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <title>#{get 'title' /}</title>		
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/main.css'}" />
        <link rel="shortcut icon" type="image/png" href="@{'/public/images/favicon.png'}" />
        <script src="@{'/public/javascripts/jquery-1.3.2.min.js'}"></script>
        <script src="@{'/public/javascripts/jquery.tools.min.js'}"></script>
    </head>
    <body>
        <div id="header">
            <div id="logo">
                yabe with Japid, the Power Server
            </div>
            <ul id="tools">
                <li>
                    <a href="@{Admin.index()}">Log in to write something</a>
                </li>
            </ul>
            <div id="title">
                <span class="about">About this blog</span>
                <h1><a href="@{Application.index()}">${renderArg("blogTitle")}</a></h1>
                <h2>${renderArg("blogBaseline")}</h2>
            </div>
        </div>

        <div id="main">
            #{doLayout /} 
        </div>
    </body>
</html>

It’s very close to the original html template in the Yabe sample app. The @{} tag to reverse-lookup urls is supported; the #{get} tag works the same way as in classic Play’s template; the #{doLayout/} calls out and embed the child template render result. The only exception is a object retrieval syntax.

In “classic” Play template

${blogTitle}

which is a reference to an object named blogTitle, which could have been defined in any one of many possible places in the rendering context. It turns out it has been added to a special variable called renderArgs in the controller. The dynamic nature of Play’s template engine make it very hard to trace the origins of the variable references.

In Japid, one must explicitly retrieve the object from the proper place and a lot less confusing

${renderArg("blogTitle")}

The tag

The main template invoke a tag “display”, which looks like this:

*{ Display a post in one of these modes: 'full', 'home' or 'teaser' }*

`args models.Post _p, String _as

<div class='post ${_as.equals("teaser")?"teaser":""}'>
    <h2 class="post-title">
        <a href="@{Application.show(_p.id)}">$_p.title</a>
    </h2>
    <div class="post-metadata">
        <span class="post-author">by $_p.author.fullname </span>,
        <span class="post-date">$format(_p.postedAt, "dd MMM yy")</span>
        `if (!"full".equals(_as)) { 
            <span class="post-comments">
                &nbsp;|&nbsp; $numOf(_p.comments) comment$pluralize(_p.comments).
                `if (asBoolean(_p.comments)){ 
                    , latest by $lastOf(_p.comments).author
                `}
            </span>
		`} else if (asBoolean(_p.tags)) {
            <span class="post-tags">
                - Tagged 
                #{Each _p.tags | Tag _ }
                    <a href="@{Application.listTagged(_.name)}">$_</a>${_isLast?"":", "}
                #{/}
            </span>
        `}
    </div> 
    `if (!"teaser".equals(_as)){
        <div class="post-content">
            <div class="about">Detail: </div>
            $nl2br(_p.content)
        </div>
    `}
</div>

`if ("full".equals(_as)){
    <div class="comments">
        <h3>$numOf(_p.comments) comment$pluralize(_p.comments)</h3>

        #{Each _p.comments | Comment _}
            <div class="comment">
                <div class="comment-metadata">
                    <span class="comment-author">by $_.author</span>
                    <span class="comment-date">$format(_.postedAt, "dd MMM yy")</span>
                </div>
                <div class="comment-content">
                    <div class="about">Detail: </div>
                    $nl2br(escape(_.content))
                </div>
            </div>
        #{/}
    </div>
`}

There are a few interesting Japid special syntax in the above code.

1. Tags take arguments exactly like a regular template.
2. There are quite a few utility methods in Play’s JavaExtensions class that by default have been statically imported to any templates, so this statement works:


$format(_p.postedAt, “dd MMM yy”)

So do pluralize, nl2br, escape, etc. There are a few other static methods, such as asBoolean(), lastOf(), that are defined in Japid utility class. Please browser the generated display.java to find out the detail.

3. The Each tag
The Each tag as in

 #{Each _p.tags | Tag _ }
    <a href="@{Application.listTagged(_.name)}">$_</a>${_isLast?"":", "}
 #{/}

is a tag defined in Japid package. It’s very similar to the #{list} tag in Play’s templates. However since Each tag defines new variables for the body to consume, a special syntax, i.e., the vertical line, is introduced to separate the arguments to the Each tag and the argument the tag passes back to the loop body. The syntax is similar to the that in Groovy or Ruby to pass a closure to a function invocation.

The Each tag also passes a few other loop supporting objects to the body, such as _index, _parity, _isFirst, _isLast, etc. The names are fixed regardless of the element name passed in to the body.