templates.docs.templatingEngine.html Maven / Gradle / Ivy
Show all versions of spincast-website Show documentation
{#==========================================
Docs : "Templating Engine"
==========================================#}
Templating Engine
The Templating Engine (also called view engine, or template engine), is the component that you
use to generate dynamic text content. It can be used for multiple purposes but its
most frequent use is to generate HTML
pages.
The default Templating Engine included with Spincast by default is Pebble.
Using the Templating Engine
To evaluate a template, you can inject the TemplatingEngine
component anywhere you need it. But the preferred way to generate HTML
pages is to use the
sendTemplateXXX(...)
methods on the response()
add-on :
public void myRouteHandler(AppRequestContext context) {
JsonObject model = context.response().getModel();
// ... adds variables to the model
// Renders the response model using a template
// and sends the result as HTML
context.response().sendTemplateHtml("/templates/myTemplate.html");
}
You can also evaluate a template without sending it as the response. The
templating()
add-on give you direct access to the Templating Engine.
Here's an example where you manually evaluate a template to generate the content
of an email :
public void myRouteHandler(AppRequestContext context) {
User user = getUser();
JsonObject params = context.json().create();
params.set("user", user);
String emailBody = context.templating().fromTemplate("/templates/email.html", params);
// ... do something with the content
}
Note that, by default, the path to a template is a classpath path. To
load a template from the file system instead, use false
as the
"isClasspathPath"
parameter :
public void myRouteHandler(AppRequestContext context) {
User user = getUser();
JsonObject params = context.json().create();
params.set("user", user);
String emailBody = context.templating().fromTemplate("/templates/email.html",
false, // From the file system!
params);
// ... do something with the content
}
Finally you can evaluate an inline template :
{% verbatim %}
public void myRouteHandler(AppRequestContext context) {
// We can use a standard Map<String, Object> instead
// of a JsonObject for the parameters
Map<String, Object> params = new HashMap<String, Object>();
params.set("name", "Stromgol");
// This will be evaluated to "Hi Stromgol!"
String result = context.templating().evaluate("Hi {{name}}!", params);
// ... do something with the result
}
{% endverbatim %}
Templates basics (using Pebble)
The syntax to use for your templates depends on the Templating Engine implementation.
Here, we'll show some examples using the default Templating Engine, Pebble.
Make sure you read the Pebble documentation
if you want to learn more...
Using the response model
If you are using the default way to render an HTML
page, suing the
response().sendTemplateHtml(...)
method, you can use the
response model as a container for the
parameters your template needs. The response model becomes the root of all
available variables when your template is rendered. For example, your
Route Handler
may look like :
public void myRouteHandler(AppRequestContext context) {
// Gets the response model
JsonObject model = context.response().getModel();
// Creates a "user" on adds it to the
// response model
JsonObject user = context.json().create();
user.set("name", "Stromgol");
model.set("user", user);
// Renders a template and sends it as HTML
context.response().sendTemplateHtml("/templates/myTemplate.html");
}
The template, located on the classpath (at
"src/main/resources/templates/myTemplate.html"
in a Maven project) may look like this :
{% verbatim %}
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>My application</title>
</head>
<body>
<h1>Hello {{user.name}}!</h1>
</body>
</html>
{% endverbatim %}
Using JsonPaths
When accessing the variables in a template, you can use JsonPaths.
Here are some examples :
{% verbatim %}
-
{{user.name}}
: The "name" attribute on the user object.
-
{{user.books[2].title}}
: The "title" attribute of the third book of the user object.
-
{{user['some key']}}
or {{user["some key"]}}
: The "some key" attribute
of the user object. Here brackets are required because of the space in the key.
{% endverbatim %}
Default templating variables
Spincast automatically provides some variables that can be used
when rendering a template. Those variables will always be available
to any template rendering (except if you are not in the scope of an HTTP request).
Spincast adds those variables using a "before" Filter
:
addDefaultGlobalTemplateVariables(...)
The provided variables are :
-
"spincast.pathParams" : The parameters parsed from the path of the request. To be used
like {% verbatim %}
{{pathParams.myParam}}
{% endverbatim %}.
-
"spincast.qsParams" : The parameters parsed from the queryString of the request. Note that
a single queryString variable may contain more than one values. To access the first value, use something like :
{% verbatim %}
{{qsParams.myParam[0]}}
{% endverbatim %}.
-
"spincast.cookies" : The current
Cookies. To be used
like {% verbatim %}
{{cookies.myCookie.value}}
{% endverbatim %}.
-
"spincast.requestScopedVars" : The request scoped variables added by the
various Route Handlers. To be used
like {% verbatim %}
{{requestScopedVars.myVar}}
{% endverbatim %}.
-
"spincast.langAbrv" : The abreviation of the current Locale to use. For example :
"en"
.
-
"spincast.cacheBuster" : The current cache buster code.
-
"spincast.routeId" : The id of the current route (of its main Route Handler).
-
"spincast.fullUrl" : The full URL of the current request.
-
"spincast.isHttps" : Is the current URL secure (HTTPS)?
-
"spincast.alerts" : The Alert messages, if any. Those
also include Flash messages (Spincast automatically converts Flash messages
to Alert messages). They also contain Alert messages that you may have
explictly added using the
addAlert(...)
method of the response()
add-on. For example :
public void myRouteHandler(AppRequestContext context) {
context.response().addAlert(AlertLevel.ERROR, "Some message");
}
Layout
If you are building a traditional website and use templates to render HTML
,
make sure you read the
"Template Inheritance",
"extends" and
"include" sections
of the Pebble documentation to learn how to create a layout for your website! This is an important foundation for
a scalable website structure.
{% verbatim %}
You can browse this
Spincast website sources
themselves to see how we use such layout using some {% block %}
.
The layout.html
file is the root of our layout.
{% endverbatim %}
Provided functions and filters
Spincast provides some
functions
and
filters
for Pebble out of the box. They are defined in the
SpincastMainPebbleExtensionDefault class.
Functions
-
get(String pathExpression)
This function receives the path to an element as a string, evaluates it, and
returns the element if it exists or null
otherwise.
In other words, it allows you to dynamically create the path to an element. For example :
{% verbatim %}
{% set user = get("myForm.users[" + generateRandomPosition() + "]") %}
{% if user is not null %}
<p>The name of the random user is {{user.name}}</p>
{% endif %}
{% endverbatim %}
-
msg(String messageKey, ...params)
This function displays a localized message taken from the
Dictionary.
Only the message key
is required, but you can also pass some parameters to be
evaluated.
Example, without any parameters :
{% verbatim %}
<h1>{{ msg('app.home.title') }}</h1>
{% endverbatim %}
With parameters :
{% verbatim %}
<div>{{ msg('welcome.user', 'firstName', user.firstName, 'lastName', user.lastName) }}</div>
{% endverbatim %}
Note that each parameter's key must have an
associated value or an exception will be thrown (the number of parameters must be even).
Finally, if the first parameters is true
, the evaluation of the message
will be forced, even if no parameters are provided. Indeed, to improve performance, by default a message
from the dictionary is only evaluated using the Templating Engine
if at least one parameter is provided.
Example of forcing the evaluation:
{% verbatim %}
<h1>{{ msg('app.display.date', true) }}</h1>
{% endverbatim %}
-
jsOneLine(String code)
This function allows the output of javascript code inside quotes.
It removes newlines and properly escapes the quotes in the code.
{% verbatim %}
let js="{{jsOneLine(code)}}";
{% endverbatim %}
You can pass true
as a second parameter if single quotes needs to be
escaped instead of double quotes:
{% verbatim %}
let js='{{jsOneLine(code, true)}}';
{% endverbatim %}
-
querystring(String querystring)
This function will add the specified querystring
to the existing one. In other words, the querystring of the current
request will be kept, but the specified one will be concatenated to it.
If a parameter name already exist in the current querystring, it is overwritten.
{% verbatim %}
<a href="{{ querystring('?offset=' + newOffset) }}">link</a>
{% endverbatim %}
If the previous example was evaluated part of a
"https://example.com?limit=10
" request,
the resulting content would be something like
"<a href="?limit=10&offset=10">link</a>
"
Finally, note that if this function is called without being inside a
request context, the specified querystring will simply be used as is.
-
querystringToHiddenFields(String querystring)
This function takes all the parameters of the current querystring and converts
them to hidden fields (ie: <input type="hidden" name="xxx" value="yyy" />
).
This mainly allows a GET
form to keep current parameters when it is submitted.
For example:
{% verbatim %}
<form class="resultsCtrlWrap" method="get">
{{ querystringToHiddenFields() }}
<input type="submit" value="Submit!" />
</form>
{% endverbatim %}
If this form was displayed on a /myPage?someKey=someValue
url,"someKey=someValue
" would
be on the url the form would be submitted to.
The function takes an optional parameter which is a list of parameters to ignore. For example:
{% verbatim %}
{{ querystringToHiddenFields(['orderBy', 'filter']) }}
{% endverbatim %}
-
isRoute(String path, [boolean isRegEx, boolean allowSubPaths])
This function returns true
if the specified path matches the
route of the current request.
For example:
{% verbatim %}
<span class="menu {% if isRoute('/users') %}active{% endif %}"</span>
{% endverbatim %}
If the second parameter is "true
", the specified path will
be considered as a regular expression:
{% verbatim %}
<span class="menu {% if isRoute('/(user|users)', true) %}active{% endif %}"</span>
{% endverbatim %}
Finally, if the third parameter is "true
", any subpath of the specified
path will also match! If the specified path is a regular expression,
then "(/?$|/.*)
" will be concatenated to it. If the path is not
a regular expression, Spincast will use "startWith(path)
" instead of
"equals(path)
" to validate the current route:
{% verbatim %}
// Will match "/users", "users/123", "users/123/books/456"
<span class="menu {% if isRoute('/users', false, true) %}active{% endif %}"</span>
// Will match "/user", "/user/123/books/456", "/users/", "/users/123/books/456"
<span class="menu {% if isRoute('/(user|users)', true, true) %}active{% endif %}"</span>
{% endverbatim %}
If this function is evaluated outside of a request context (for example from a scheduled task),
then false
is returned.
-
isRouteId(String routeId)
This function returns true
if the specified id is the
id of the current route.
For example:
{% verbatim %}
<span class="menu {% if isRouteId('myUsersRouteId') %}active{% endif %}"</span>
{% endverbatim %}
If this function is evaluated outside of a request context (for example from a scheduled task),
then false
is returned.
Filters
-
pathExpression | get()
This filter does the same as the get()
function :
it receives the path to an element as a string, evaluates it, and
returns the element if it exists or null
otherwise.
The difference with the get()
function is that you can use undefined
elements with this filter and no exception is going to be thrown, even if
strictVariables
is on.
{% verbatim %}
{% set user = "may.not.exist.users[" + generateRandomPosition() + "]" | get() %}
{% if user is not null %}
<p>The name of the random user is {{user.name}}</p>
{% endif %}
{% endverbatim %}
-
someText | newline2br()
This filter will replace the newlines of the text with <br />\n
. This is
useful when you want to display some text in an HTML
template while
respecting the newlines.
{% verbatim %}
{{ someText | newline2br }}
{% endverbatim %}
By default, the rest of the text will be properly escaped. For example, "<em>a\nb</em>
" will
become "<em>a<br />\nb</em>
".
To disable the escaping, pass false
as a parameter:
{% verbatim %}
{{ someText | newline2br(false) }}
{% endverbatim %}
This would result in "<em>a<br />\nb</em>
".
-
someVar | boolean()
This filter converts a "true"
or "false"
string to
a proper boolean. This allows the string variable to be used in if
statements.
{% verbatim %}
// Let's say val is "true" (a string) here...
{% if val | boolean %}ok{% endif %}
{% endverbatim %}
The main use case for this filter is when a form is submitted and contains a boolean
field which is transformed to a string value. When redisplaying the form, you
may need to interpret the value of the field as a true boolean to perform logic.
If the variable is already a boolean, it will also work fine.
-
someElements | checked(String[] matchingValues)
This filter outputs the string "checked"
if at least
one element from someElements
matches one of the element from
the matchingValues
. Both sides can either be a single element or
an array of elements. For example :
{% verbatim %}
<label for="drinkTea">
<input type="radio"
id="drinkTea"
name="user.favDrink"
{{user.favDrink | checked("tea")}}
value="tea"/> Tea</label>
{% endverbatim %}
Note that the elements are compared using
equivalence,
not using equality. So the String "true"
matches the true
boolean and "123.00"
matches 123
, for example.
-
someElements | selected(String[] matchingValues)
This filter outputs the string "selected"
if at least
one element from someElements
matches one of the element from
the matchingValues
. Both sides can either be a single element or
an array of elements. For example :
{% verbatim %}
<select name="user.favDrink" class="form-control">
<option value="tea" {{user.favDrink | selected("tea")}}>Tea</option>
<option value="coffee" {{user.favDrink | selected("coffee")}}>Coffee</option>
<option value="beer" {{user.favDrink | selected("beer")}}>WBeer</option>
</select>
{% endverbatim %}
Note that the elements elements are compared using
equivalence,
not using equality. So the String "true"
matches the true
boolean and "123.00"
matches 123
, for example.
The remaining filters are all about validation. Make sure you read
the dedicated Validation Filters section to learn more
about them and to see some examples!
-
ValidationMessages | validationMessages()
This filter uses a template fragment to output the
Validation Messages
associated with a field.
-
ValidationMessages | validationGroupMessages()
This filter is similar to validationMessages()
but uses a different
template. It is made to output the Validation Messages
of a group of fields,
instead of a single field.
-
ValidationMessages | validationClass()
The validationClass(...)
filter checks if there are
Validation Messages
and, if so, it outputs a class name.
-
ValidationMessages | validationFresh()
ValidationMessages | validationSubmitted()
Those two filters are used to determine if a form is displayed for the first time,
or if it has been submitted and is currently redisplayed with
potential Validation Messages
.
-
ValidationMessages | validationHasErrors()
ValidationMessages | validationHasWarnings()
ValidationMessages | validationHasSuccesses()
ValidationMessages | validationIsValid()
Those four filters check if there are Validation Messages
of a
certain level and return true
or false
.