Skip to main content
Skip table of contents

Writing your own PocketQuery Templates

PocketQuery Templates help you visualize results of your Queries. When you execute a Query, the result from the Datasource is passed through a Converter (Writing your own PocketQuery Converters), and then the result from the Converter is passed to a Template.

For the most basic use cases there is a default Template which displays your result in a simple table. However, if you have a complicated Query with a structured result, or you just want to add some fancy visualization/effects, the basic Template might not be enough. Thus, in this tutorial, we will look more closely at PocketQuery Templates. You can find the following here:

Apache Velocity Templates

If you are interested in technical details of Apache Velocity Templates, you can refer to the official resource. In this page, we will focus solely how they are used in the context of PocketQuery.

A Velocity Template (VT) is a template written in the the Velocity Template Language (VTL). Which is commonly used to generate HTML. It lets you define and use variables, which are marked with $, and you can also include simple logic (such as foreach, if-else, and more). The templating engine is written in Java. Consequently, you can see many similarities with the Java programming language; e.g., Velocity template variables are Java objects. You can access their attributes using the . notation. For example, let’s consider an instance cat of the following class:

JAVA
public class Cat {
  public String name;
  public String makeNoise() {
    return "Meooow!";
  }
}
  • $cat.name prints its field “name”.

  • $cat.makeNoise() calls its method “makeNoise” and prints “Meooow!”.

In the following sections, we will look at some simple PocketQuery templates and explain their syntax/semantics. If you are not familiar with HTML, we recommend to start with an HTML tutorial (for example, W3 School tutorials) to understand the basic concepts of this markup language. If you don’t have any experiences with Java, don’t worry, it is not an essential prerequisite.

1. Getting started: accessing the Query result and setting variables

Let’s start with a basic scenario. Consider the following result passed from your Converter:

CODE
{
  name: "Lively Apps",
  fullName: "Lively Apps GmbH",
  founded: 2020
}

We can create a simple Template displaying this object:

HTML
<p>Our company is $result.name.</p>
<p>It was founded in $result.founded.</p>

The result displayed after macro execution is:

HTML
Our company is Lively Apps.
It was founded in 2020.

Explanation: Your Converter result is accessible through the $result object. We want to access the fields “name” and “founded”. Thus, we use the dot notation to access these fields: $result.name, $result.founded.

We might want to show the same value multiple times, such as:

HTML
<p>Greetings from $result.name!<p>
<p>We are $result.name from Germany.<p>

This Template is working fine but we can make it more readable by introducing a variable. You can do that using the #set directive, for example #set($companyName = $result.name). This creates a new variable, called “companyName”, with its value being assigned to $result.name (in our case, the result is “Lively Apps”). Keep in mind that the $ before the name of the variable is essential and you have to use it for any variable name. The Template can then be changed to:

CODE
#set($companyName = $result.name)
<p>Greetings from $companyName!<p>
<p>We are $companyName from Germany.<p>

Both these Templates have the same result, which is:

TEXT
Greeting from Lively Apps!
We are Lively Apps from Germany.

You can also use this command to change the value of an existing variable, for example:

HTML
#set($companyName = $result.name)
<p>Greetings from $companyName!</p>
#set($companyName = "Lively Apps 2")
<p>We are $companyName from Germany.</p>

The result is now:

CODE
Greeting from Lively Apps!
We are Lively Apps 2 from Germany.

2. Introducing validation and conditions: if/else blocks, basic operators

Sometimes, your results might have parameters that do not necessarily contain any value (if this is the case, such a value is called null or undefined). Let’s consider the following object returned from our Converter:

JSON
{
  name: "Lively Apps",
  fullName: "Lively Apps GmbH",
  country: null,
  founded: 2020
}

We want to create a Template, which optionally (i.e. only when the value is present) displays parameter “country”. To reach this behavior, we can use an if-else construct. Its syntax is:

CODE
#if (CONDITION)
  EXECUTED WHEN CONDITION == TRUE
#else
  EXECUTED WHEN CONDITION == FALSE
#end

In our case, such a template might look like the following:

HTML
#if ($result.country)
  <p>We are from $country.</p>
#else
  <p>We won't tell you where we are from.</p>
#end

In our case, where the “country” is null, the second block will be executed, therefore, the result is:

CODE
We won't tell where we are from.

Let’s consider the opposite scenario, where the “country” is set to some value, for example country: "Germany". In that case, the first block would be executed and the result would be:

CODE
We are from Germany.

3. Accessing structured results: arrays, maps, loops

In the most of cases, your Query result is an array of elements, e.g.:

JSON
[
  {id: 1, name: "Scandio", country: "Germany"},
  {id: 2, name: "Lively Apps", country: "Germany"}
]

There are two options, how you can access these elements. To demonstrate it, let’s consider two scenarios:

  1. We want to obtain one element; the first one, it does not matter how many elements were returned in the array.

  2. We want to obtain all elements and perform some action with all of them.

In the first scenario, we can use direct access to obtain the element. We can do that using $result.get(index), where index stands for the position of the element in the array. When using this approach, keep in mind to check the size of your array. If you try to access an element that is not in the array, the execution fails with an IndexOutOfBoundsException. Example Template code could look like the following:

HTML
#if ($result.size() >= 2)
  #set ($firstElement = $result.get(0))
  #set ($secondElement = $result.get(1))
  <p>The first item is $firstElement.name and the second is $secondElement.name.</p>
#else
  <p>Not enought results :-(...</p>
#end

For our input, the result of this template would be:

CODE
The first item is Scandio and the second is Lively Apps.

Sometimes, we are not interested in the exact number or order of our elements but simply want to print all of them. To do so, we can use a loop. In our case, we can use foreach loop to access all elements in the array, one by one. Such a construct has the following format:

CODE
#foreach (ELEMENT in ARRAY)
  CODE THAT IS EXECUTED FOR EACH ELEMENT IN THE ARRAY
#end

In our case, we can do the following:

HTML
#foreach ($element in $result)
  <p>ID: $element.id, name: $element.name</p>
#end

The result is then:

CODE
ID: 1, name: Scandio
ID: 2, name: Lively Apps

In some cases, we may want to iterate over an object’s properties rather than the elements in an array. To do so, we can use the same constructs, with a little modification. Let’s consider a situation, where we don’t exactly know which parameters our results have, and we want to print all of them. This can be achieved by introducing another (nested) loop.

Loops iterating over properties are a bit different. They have the following structure:

HTML
#foreach ($entry in $object.entrySet())
#end

When you create an entry set from an object, you simply convert its properties to an array. For example, consider object:

CODE
{
  id: 2, 
  name: "Lively Apps", 
  country: "Germany"
}

The .entrySet() method converts it to:

CODE
[
  {key: "id", value: 2},
  {key: "name", value: "Lively Apps"},
  {key: "country", value: "Germany"}
]

Thus, a loop over our entries has 3 iterations, one for each property.

Let’s demonstrate it with the following example, so we can visualize what we mean by the “nested loop”:

HTML
#set ($counter = 1)
#foreach ($element in $result)
  <p>$counter:</p>
  #foreach ($property in $element.entrySet())
    <p>$property.key - $property.value</p>
  #end
  #set ($counter = $counter + 1)
#end

Let’s go through this example line by line:

  1. We set the variable “counter” to “1”, so we are able to print the index of the element that is processed.

  2. We loop over all elements in the “result” array.

  3. We started element processing, so we print the “counter” variable.

  4. We loop over all properties for the current element from “result” array.

  5. We print the property name ($property.key) and property value ($property.value).

  6. We increment “counter”.

  7. end does not have any logical meaning, it marks the end of the loop.

The result of execution for our example is:

CODE
1:
id - 1
name - Scandio
country - Germany
2:
id - 2
name - Lively Apps
country - Germany

The template above can be simplified. Apache velocity offers a variable $velocityCount (documentation), which is natively present in each loop. It starts at 1 and automatically increments each iteration. This piece of code generates the same output:

HTML
#foreach ($element in $result)
  <p>$velocityCount:</p>
  #foreach ($property in $element.entrySet())
    <p>$property.key - $property.value</p>
  #end
#end

Alternatively, you can iterate only keys or only values of element properties using .keySet() and .values(). In our scenario, we can obtain all values (alternatively all keys) using:

HTML
#foreach ($value in $result.values())
  <p>$value</p>
#end

Or for all keys:

CODE
#foreach ($key in $result.keySet())
  <p>$key</p>
#end

Which results in the following (when using .values()):

CODE
id
name
country

Or eventually (when using .keySet()):

CODE
2
Lively Apps
Germany

4. PocketQuery helpers

In a real use case scenario, you might need to visualize some complex values. By “complex values” we can understand for example dates, currencies, percentages, etc. Maybe you also want to perform some operations over results, such as sorting, making elements unique, or reversing the order of elements. PocketQuery introduces a set of helpers, which will help you format the values:

On this page, you can also find helpers that can be used to execute nested queries (i.e., you can execute another query inside a template) or nested templates (i.e., you can render another template inside a template).

5. Default PocketQuery Template explanation

If you don’t choose differently, your Queries always use the default PocketQuery Template. It looks as follows:

HTML
#set($tableRows = $PQ.toList($result))
#set($tableHeaders = $tableRows.get(0).keySet())
<table class="aui">
  <thead>
    <tr>
      #foreach ($header in $tableHeaders)
        <th>$!header</th>
      #end
    </tr>
  </thead>
  <tbody>
    #foreach ($row in $tableRows)
      <tr>
        #foreach ($column in $row)
          <td>$!column</td>
        #end
      </tr>
    #end
  </tbody>
</table>

Explanation: On the first line, we obtain the query results and flatten them (using $PQ.toList helper). On the second line, we obtain header columns by looking at the first element in the result array and obtaining its keys only (more detailed description of .keySet() is in the loops section). Then we construct a table header (starting on line 4) by iterating through parameter keys and printing them. Note that we used the $! sign, it denotes that the $header variable is printed only in the case where it is not null/undefined. From line 11, we print the table body. We iterate over all elements in the results array and print their values (in case it is present).

Even though the Velocity Templating Language is immensely powerful, it is always a good idea to first bring your Query result into shape using a Converter. Converters are written in an actual programming language (JavaScript) and are therefore much easier to use for performing complex operations on data. If you want to learn more, continue with: Writing your own PocketQuery Converters.

Build your own Template

In this page, we described basics of Apache Velocity Templates and how we can use them to present PocketQuery results. Now you can apply what you’ve learned and polish your query results to perfection. Do you have any questions? Issues? Feedback? Don’t hesitate to contact our support!

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.