Using Templates

Template as a Service

Just to makes you understand how the template system works, you should read these small paragraphs.

Wisdom allows the integration of any template engine. By default, Thymeleaf is provided. Each template is exposed as a service and can be injected into a controller using the @View annotation. This pattern is used regardless of the template engine you use.

The View annotation is looking for a template with the specified name. It is equivalent to a regular @Requires with a filter.

The Thymeleaf Template Engine

Thymeleaf is an XML / XHTML / HTML5 template engine (extensible to other formats) that can work both in web and non-web environments. It is really well suited for serving XHTML/HTML5 at the view layer of web applications, but it can also process any XML file even in offline environments.

The main goal of Thymeleaf is to provide an elegant and well-formed way of creating templates. It allows you to create powerful natural templates, that can be correctly displayed by browsers and therefore work also as static prototypes. One of the main interests of Thymeleaf is a smooth collaboration with a Web Designer. By using specific attributes on HTML elements, you never interfere with the work of the web designers. They create the HTML page with whatever structure, and CSS tricks, and you just identify the parts where you inject the data.

It looks like this. As you can see, it’s a pretty straightforward syntax supporting all the features you expect from a template engine: variables, internationalization, iterations, layouts, fragments, object navigation, and much more.

<table>
  <thead>
    <tr>
      <th th:text="#{msgs.headers.name}">Name</th>
      <th th:text="#{msgs.headers.price}">Price</th>
    </tr>
  </thead>
  <tbody>
    <tr th:each="prod : ${allProducts}">
      <td th:text="${prod.name}">Oranges</td>
      <td th:text="${#numbers.formatDecimal(prod.price,1,2)}">0.99</td>
    </tr>
  </tbody>
</table>

The complete documentation of Thymeleaf is available here.

My First Template

Thymeleaf templates are HTML files with the .thl.html extension, for example: mytemplate.thl.html. The "mytemplate" is referred as the template name. Templates can be placed in:

  • src/main/resources/templates and are embedded in the application’s Jar.

  • src/main/templates and are in the distribution but not in the application’s Jar file.

Whether or not your application is intended to be reused in several bigger application determines the place of your template files.

Let’s look at the following template, named welcome.thl.html:

include::{resourcedir}/templates/snippets/doc/welcome.thl.html[]

The th:text attribute instructs the template engine to replace the content of the element by the welcome value. This value is given as parameter to the template engine.

So now in your controller, use the @View annotation to inject the template:

include::{sourcedir}/controllers/templates/WelcomeController.java[tags=controller]
  1. Use the @View annotation and indicate the template name

  2. The injected field must use the Template type

  3. Use the render(template, ...) methods to render the template

You can render the template using the render methods:

  • render(Template template) asks the template engine to render the template without input variables

  • render(Template template, Map<String, Object> params) inserts the given parameters to the template

  • render(Template template, Object... params) proposes an easier way to pass parameters. However, one parameter out of two must be a string (the parameter name), while the following is the value:

    return ok(render(template, "welcome", "hello", "age", 1, "colors", new String[] {"red", "blue"}));

The render method is wrapped within an ok method indicating the type of response. Obviously, you can use any other type of result.

Some values are automatically given to the template engine:

  • the parameters of the request

  • the values stored in the session

  • the value stored in the flash scope

Some Thymeleaf constructions

Now we have seen how template can be used, let’s see some general construction patterns we use in templates:

Variable:

<p th:text="${variable}">this will be replace by the variable</p>
<p th:utext="${variable}">this will be replace by the variable (unescaped so won't remove the
contained HTML elements)
</p>

Internationalized variable:

<p th:text="#{variable}">this will be replaced by the variable using the request locale</p>
Warning
Internationalized values are directly retrieved from the internationalization service from Wisdom.

Iteration:

You can iterate on collections, arrays, maps…

<!-- Iterate over a list of products -->
<tr th:each="prod : ${prods}">
        <td th:text="${prod.name}">Onions</td>
        <td th:text="${prod.price}">2.41</td>
        <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

Conditionals:

<td th:text="${prod.inStock}? 'true' : 'false'">
    true or false according to the prod.isStock value
</td>
<a href="#" th:if="${not #lists.isEmpty(prod.comments)}">display this link comments if not empty</a>
Tip
Check the Thymeleaf documentation to find more.

Fragments and Layout

Templates are generally used to create layouts. Thymeleaf allows creation of fragments, and to include them in the main template.

Fragments are an identified part of a template. For example, the following snippet defines two fragments: my-content and sub-content.

include::{resourcedir}/templates/snippets/doc/content.thl.html[]

Fragments can be included either using:

*th:include: include the fragment under the element using the directive *th:replace: replace the element using this directive with the fragment content

The value of theses directive is formed by `name_of_the_template_defining_the_fragments

fragment_name`. For example, if the previous fragment are defined in content.thl.html, including them would look like:

include::{resourcedir}/templates/snippets/doc/page.thl.html[]

TIPS: fragments can use any variable given to the template engine.

Using the router from the template

A template can use a special object named #routes to retrieve the url of action methods. In the following snippet, the action attribute is assigned to the url of the upload method of the current controller (the controller having requested the template rendering).

th:attr="action=${#routes.route('upload')}"

You can also ask for the url of an action method receiving parameters:

th:attr="action=${#routes.route('upload', 'param', 'value', 'param2', 'value2')}"

Finally, you can target methods from other controllers with:

th:attr="action=${#routes
    .route(`org.acme.controller.MyOtherController`, 'upload', 'param', 'value')}"

If the route cannot be found, the template rendering fails.

The routes object is also able to compute the URL of assets:

<link th:href="${#routes.asset('css/bootstrap.min.css')}" rel="stylesheet"/>
<link th:href="${#routes.asset('/css/bootstrap-theme.min.css')}" rel="stylesheet"/>
<script th:src="${#routes.asset('/jquery.js')}"></script>
<script th:src="${#routes.asset('js/bootstrap.min.js')}"></script>

It locates the assets in the assets directories and in webjars. With such a functionality, you don’t need to write the complete urls. In addition, if an asset cannot be located, the template rendering fails.