jinja-js
Version:
JavaScript templating engine based on Jinja2
322 lines (236 loc) • 11.2 kB
Markdown
#Template Designer Documentation
This document describes the syntax and semantics of the template engine and
will be most useful as reference to those creating Jinja templates. Most of
this content is adapted from the official [Jinja2 Docs][jinjadocs].
## Synopsis
A template is simply a text file. It can generate any text-based format
(HTML, XML, CSV etc.). It doesn't have a specific file extension, `.html`
or `.xml` are just fine.
A template contains **variables** and **expressions**, which get replaced with
values when the template is evaluated, and tags, which control the logic of
the template. The template syntax is heavily inspired by Django and [almost
completely compatible][liquid_compatibility] with [Liquid][liquid].
Below is a minimal template that illustrates a few basics. We will cover
the details later in that document:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="">{{ item.caption }}</a></li>
{% endfor %}
</ul>
<h1>My Webpage</h1>
{{ a_variable }}
</body>
</html>
There are two kinds of delimiters. `{% ... %}` and `{{ ... }}`. The first
one is used to execute statements such as for-loops or assign values, the
latter prints the result of the expression to the template.
## Variables
The application passes variables to the templates you can mess around in the
template. Variables may have properties or elements on them you can access
too. How a variable looks, heavily depends on the application providing
those.
You can use dot-notation (`.`) to access properties of a variable, or the
"subscript" syntax (`[]`) can be used. The following lines do the same:
{{ foo.bar }}
{{ foo['bar'] }}
It's important to know that the curly braces are *not* part of the variable.
If a variable or property does not exist you will get back `undefined` which
will evaluate to an empty string if printed. Jinja will not throw an error
if you try to access a property on undefined, but return undefined.
Implementation
In Jinja `foo.bar` (or `foo['bar']`) does the following:
* check if there is a property *bar* on *foo*.
* if there is not, check if there is a method `_get` on *foo*.
* if so, call `_get('bar') and save the result on the property
*bar* so we don't have to call _get again next time.
* if there is not, return undefined.
## Filters
Variables can be modified by **filters**. Filters are separated from the
variable by a pipe symbol (`|`) and may have optional arguments in
parentheses (or using the "liquid" syntax). Multiple filters can be
chained. The output of one filter is applied to the next.
`{{ name|striptags|upcase }}` for example might remove all HTML Tags from the
*name* and then uppercase it. Filters that accept arguments have parentheses
around the arguments, like a function call:
`{{ list|join(', ') }}`.
or the alternate "liquid" syntax with a colon:
`{{ list|join: ", " }}`.
The only built-in filters are `html` and `safe` for use with escaping, but
you can add filters passed to render(data, options) as part of the options
argument.
## Output Literals
Jinja will honor anything in a string literal (single- or double-quoted) so
the following will output a "}}" without treating it as part of a token:
`{{ '}}' }}`
## Template Inheritance
The most powerful part of Jinja is template inheritance. Template inheritance
allows you to build a base "skeleton" template that contains all the common
elements of your site and define **blocks** that child templates can override.
Sounds complicated but is very basic. It's easiest to understand it by starting
with an example.
### Base Template
This template, which we'll call `base`, defines a simple HTML skeleton
document that you might use for a simple two-column page. It's the job of
"child" templates to fill the empty blocks with content:
<!DOCTYPE html>
<html lang="en">
<head>
<title>{{ title }} - My Website</title>
{% block head %}
<link rel="stylesheet" href="style.css" />
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
(c) Copyright 2013 by <a href="http://example.com/">you</a>.
{% endblock %}
</div>
</body>
</html>
In this example, the `{% block %}` tags define four blocks that child templates
can fill in. All the *block* tag does is to tell the template engine that a
child template *may* override those portions of the template.
### Child Template
A child template might look like this:
{% set title = "Home" %}
{% extends "base" %}
{% block head %}
<link rel="stylesheet" href="home.css" />
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome on my awesome homepage.
</p>
{% endblock %}
The `{% extends %}` tag is the key here. It tells the template engine that
this template "extends" another template. When the template system evaluates
this template, first it locates the parent. The extends tag should be before
any content or blocks. Everything before it is printed out normally and
may cause confusion. However set/assign statements can be before it to set
variables that will be available inside the parent template.
Note that since the child template doesn't define the `footer` block, the
value from the parent template is used instead.
The name of the template must be a string literal and will be passed to the
template loader. For example the `FileSystemLoader` allows you to access
other templates by giving the filename. You can pass any string including
with path-like name that may be interpreted by your *readTemplateFile*
method:
{% extends "layout/main" %}
But this behavior can depend on the application's *readTemplateFile* method.
You can't define multiple `{% block %}` tags with the same name in the
same template. This limitation exists because a block tag works in "both"
directions. That is, a block tag doesn't just provide a hole to fill - it
also defines the content that fills the hole in the *parent*. If there
were two similarly-named `{% block %}` tags in a template, that template's
parent wouldn't know which one of the blocks' content to use.
### Block Nesting
Blocks can not be nested at this time. This feature is not implemented and
may cause unpredictable results.
## HTML Escaping
When generating HTML from templates, there's always a risk that a variable will
include characters that affect the resulting HTML. There are two approaches:
manually escaping each variable or automatically escaping everything by default.
Jinja supports both, but what is used depends on the application configuration.
The default configuration is automatic escaping.
### Working with Automatic Escaping
When automatic escaping is enabled (on by default) everything is escaped except
expressions explicitly marked as safe. Those are marked by using the *|safe*
filter `{{ user.description | safe }}` or by using the "liquid" syntax
`{{{ user.description }}}` both of which are equivelent.
### Working with Automatic Escaping Disabled
If automatic escaping is disabled it's **your** responsibility to escape
variables if needed. What to escape? In HTML, if you have a variable that
*may* include any of the following chars (`>`, `<`, `&`, or `"`) you
must escape it unless the variable contains well-formed and trusted
HTML. As a rule of thumb, you should escape anything that came from an
outside source (like a form post or on the query-string) to help prevent
against cross-site scripting attacks. Escaping works by piping the
variable through the `|html` filter: `{{ user.username | html }}`.
## List of Control Structures
A control structure refers to all those things that control the flow of a
program - conditionals (i.e. if/elseif/else), for-loops, as well as things like
macros and blocks. Control structures appear inside `{% ... %}` blocks
in the default syntax.
### For
Loop over each item in a sequence. For example, to display a list of users
provided in a variable called *users*:
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username }}</li>
{% endfor %}
</ul>
Inside of a for loop block you can access some special variables:
- *loop.index*: The current iteration of the loop. (1 indexed)
- *loop.index0*: The current iteration of the loop. (0 indexed)
- *loop.first*: True if first iteration.
- *loop.last*: True if last iteration.
- *loop.length*: The number of items in the sequence.
It's not possible to *break* or *continue* in a loop. You can however
filter the sequence during iteration which allows you to skip items.
The following example skips all the users which are hidden:
{% for user in users %}
{% if not user.hidden %}
<li>{{ user.username }}</li>
{% endif %}
{% endfor %}
If no iteration took place because the sequence was empty you can render a
replacement block by using *else*:
<ul>
{% for user in users %}
<li>{{ user.username }}</li>
{% else %}
<li><em>no users found</em></li>
{% endfor %}
</ul>
### If
The *if* statement in Jinja is just like the if statement in JavaScript but
the parentheses are optional. In the simplest form you can use it to test
if a variable is defined and not falsy (null, undefined, 0 or empty
string):
{% if users %}
<ul>
{% for user in users %}
<li>{{ user.username }}</li>
{% endfor %}
</ul>
{% endif %}
For multiple branches *elseif* and *else* can be used (for compatibility
*elif* can be used as an alternative to *elseif*). You can use more complex
expressions there too:
{% if kenny.sick %}
Kenny is sick.
{% elseif kenny.dead %}
You killed Kenny!
{% else %}
Kenny looks okay --- so far
{% endif %}
### Extends
The *extends* tag can be used to extend a template from another one. You
can have multiple of them in a file but only one of them may be executed
at the time. See the section *Template
Inheritance* above.
### Block
Blocks are used for inheritance and act as placeholders and replacements
at the same time. They are documented in detail as part of the section
*Template Inheritance* above.
### Include
The *include* statement is useful to include a template and return the
rendered contents of that file into the current namespace:
{% include 'header' %}
Body
{% include 'footer' %}
Included templates have access to the variables of the active context by
default.
[jinjadocs]: http://jinja.pocoo.org/docs/templates/
[liquid]: http://liquidmarkup.org/
[liquid_compatibility]: /compatibility.md