{#==========================================
Docs : "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
The default Templating Engine included with Spincast by default is Pebble.
To evaluate a template, you can inject the TemplatingEngine
component anywhere you need it. But the preferred way to generate
Templating Engine
HTML pages.
Using the Templating Engine
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 %}
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...
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");
}
"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 %}
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.
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 :
{{pathParams.myParam}}{% endverbatim %}.
{{qsParams.myParam[0]}}{% endverbatim %}.
{{cookies.myCookie.value}}{% endverbatim %}.
{{requestScopedVars.myVar}}{% endverbatim %}.
"en".
addAlert(...) method of the response()
add-on. For example :
public void myRouteHandler(AppRequestContext context) {
context.response().addAlert(AlertLevel.ERROR, "Some message");
}
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 %}
Spincast provides some functions and filters for Pebble out of the box. They are defined in the SpincastPebbleExtensionDefault class.
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 %}
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.
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.
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.