Basic Usage
Syntax Reference
There are two primary delimiters used within a Pebble template: {{ ... }}
and {% ... %}
.
{{ ... }}
is used to output the result of an expression. Expressions can be very simple (ex. a variable name) or much more complex.
{% ... %}
is used to change the control flow of the template; it can contain an if-statement, define a parent template, define a new block, etc.
If needed, you can escape those delimiters and avoid Pebble templating applying on a variable thanks to the raw tag.
Variables
You can output variables directly inside an expression. For example, if the context contains a variable named foo
which is a string with the value "bar", the following will output "bar":
{{ foo }}
You can use the dot (.
) notation to access the attributes of variables. If the attribute contains a special character, you can use the subscript notation ([]
) instead.
{{ foo.bar }} # Attribute 'bar' of 'foo'
{{ foo.bar.baz }} # Attribute 'baz' of attribute 'bar' of 'foo'
{{ foo['foo-bar'] }} # Attribute 'foo-bar' of 'foo'
{{ foo['foo-bar']['foo-baz'] }} # Attribute 'foo-baz' of attribute 'foo-bar' of 'foo'
You will see a lot of tasks with hyphenated names in the documentation. When using hyphenated names, you must use the subscript notation ([]
) to access any task variables as -
is a Pebble special character.
Additionally, if foo
is a List, then foo[idx]
can be used to access the element of index idx
of the foo variable.
If the value of a variable (or of an attribute) is undefined, it will throw an error and fail the task.
Filters
Expression output can be further modified with the use of filters. Filters are separated from the variable using a pipe symbol (|
) and may have optional arguments in parentheses. Multiple filters can be chained, and the output of one filter is applied to the next.
{{ "If life gives you lemons, eat lemons." | upper | abbreviate(13) }}
The above example will output the following:
IF LIFE GI...
Functions
Whereas filters are intended to modify existing content/variables, functions are intended to generate new content. Similar to other programming languages, functions are invoked via their name followed by parentheses (()
).
{{ max(user.score, highscore) }}
The above example will output the maximum of the two variables user.score
and highscore
.
Control Structure
Pebble provides several tags to control the flow of your template, two of the main ones being the for loop, and the if statement.
{% for article in articles %}
<h3>{{ article.title }}</h3>
<p>{{ article.content }}</p>
{% else %}
<p> There are no articles. </p>
{% endfor %}
{% if category == "news" %}
{{ news }}
{% elseif category == "sports" %}
{{ sports }}
{% else %}
<p>Please select a category</p>
{% endif %}
Macros
Macros are lightweight and re-usable template fragments. A macro is defined via the macro tag:
{% macro input(type, name) %}
<input type="{{ type }}" name="{{ name }}" />
{% endmacro %}
And the macro will be invoked just like a function:
{{ input("text", "name", "Mitchell") }}
A macro does not have access to the main context; the only variables it can access are its local arguments.
Named Arguments
In filters, functions, or macros, you can use named arguments. Named arguments allow us to be more explicit on which arguments are passed and avoid mandating to pass default values.
{{ stringDate | date(existingFormat="yyyy-MMMM-d", format="yyyy/MMMM/d") }}
Positional arguments can be used in conjunction with named arguments, but all positional arguments must come before any named arguments:
{{ stringDate | date("yyyy/MMMM/d", existingFormat="yyyy-MMMM-d") }}
Macros are a great use case for named arguments because they also allow you to define default values for unused arguments:
{% macro input(type="text", name, value) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}" />
{% endmacro %}
{{ input(name="country") }}
{# will output: <input type="text" name="country" value="" /> #}
Comments
You add comments using the {# ... #}
delimiters. These comments will not appear in the rendered output.
{# THIS IS A COMMENT #}
{% for article in articles %}
<h3>{{ article.title }}</h3>
<p>{{ article.content }}</p>
{% endfor %}
Expressions
Expressions in a Pebble template are very similar to expressions found in Java.
Literals
The simplest form of expressions are literals. Literals are representations for Java types such as strings and numbers.
"Hello World"
: Everything between two double or single quotes is a string. You can use a backslash to escape quotation marks within the string."Hello #{who}"
: String interpolation is also possible using#{}
. In this example, if the value of the variablewho
is"world"
, then the expression will be evaluated to"Hello world"
.100 + 10l * 2.5
: Integers, longs and floating point numbers are similar to their Java counterparts.true
/false
: Boolean values equivalent to their Java counterparts.null
: Represents no specific value, similar to its Java counterpart.none
is an alias for null.
Collections
Both lists and maps can be created directly within the template.
["apple", "banana", "pear"]
: A list of strings{"apple":"red", "banana":"yellow", "pear":"green"}
: A map of strings
The collections can contain expressions.
Math
Pebble allows you to calculate values using some basic mathematical operators. The following operators are supported:
+
: Addition-
: Subtraction/
: Division%
: Modulus*
: Multiplication
Logic
You can combine multiple expressions with the following operators:
and
: Returns true if both operands are trueor
: Returns true if either operand is truenot
: Negates an expression(...)
: Groups expressions together
Comparisons
The following comparison operators are supported in any expression: ==
, !=
, <
, >
, >=
, and <=
.
{% if user.age >= 18 %}
...
{% endif %}
Tests
The is
operator performs tests. Tests can be used to test an expression for certain qualities. The right operand is the name of the test:
{% if 3 is odd %}
...
{% endif %}
Tests can be negated by using the is not operator:
{% if name is not null %}
...
{% endif %}
Conditional (Ternary) Operator
The conditional operator is similar to its Java counterpart:
{{ foo ? "yes" : "no" }}
Null-Coalescing Operator
The null-coalescing operator allows to quickly test if a variable is defined (exists) and to use an alternative value if not:
{% set baz = "baz" %}
{{ foo ?? bar ?? baz }}
{# results in: 'baz' #}
{{ foo ?? bar ?? raise }}
{# results: an exception because none of the 3 vars is defined #}
Operator Precedence
In order from highest to lowest precedence:
.
|
%
,/
,*
-
,+
==
,!=
,>
,<
,>=
,<=
is
,is not
and
or
Parents with Flowable Task.
When a task is in a hierarchy of Flowable Tasks, it can be complex to access the
value
of any of its parents because only the value
of the direct parent task is accessible via taskrun.value
.
To deal with this, we have included the parent
and the parents
variables.
This is illustrated in the following flow:
id: each-switch
namespace: io.kestra.tests
tasks:
- id: t1
type: io.kestra.core.tasks.debugs.Return
format: "{{task.id}} > {{taskrun.startDate}}"
- id: 2_each
type: io.kestra.core.tasks.flows.EachSequential
value: ["a", "b"]
tasks:
# Switch
- id: 2-1_switch
type: io.kestra.core.tasks.flows.Switch
value: "{{taskrun.value}}"
cases:
a:
- id: 2-1_switch-letter-a
type: io.kestra.core.tasks.debugs.Return
format: "{{task.id}}"
b:
- id: 2-1_switch-letter-b
type: io.kestra.core.tasks.debugs.Return
format: "{{task.id}}"
- id: 2-1_each
type: io.kestra.core.tasks.flows.EachSequential
value: ["1", "2"]
tasks:
- id: 2-1-1_switch
type: io.kestra.core.tasks.flows.Switch
value: "{{taskrun.value}}"
cases:
1:
- id: 2-1-1_switch-number-1
type: io.kestra.core.tasks.debugs.Return
format: "{{parents[0].taskrun.value}}"
2:
- id: 2-1-1_switch-number-2
type: io.kestra.core.tasks.debugs.Return
format: "{{parents[0].taskrun.value}} {{parents[1].taskrun.value}}"
- id: 2_end
type: io.kestra.core.tasks.debugs.Return
format: "{{task.id}} > {{taskrun.startDate}}"
As you can see, the parent
variable gives direct access to the first parent, while the parents[INDEX]
lets you access the parent deeper down the tree.
In the task 2-1-1_switch-number-2
:
{{taskrun.value}}
: mean the value of the task2-1-1_switch
{{parents[0].taskrun.value}}
or{{parent.taskrun.value}}
: mean the value of the task2-1_each
{{parents[1].taskrun.value}}
: mean the value of the task2-1_switch
{{parents[2].taskrun.value}}
: mean the value of the task2_each