Skip to content
/ qPlate Public

XML/HTML templating engine (for browsers & nodejs)

License

Notifications You must be signed in to change notification settings

Gorash/qPlate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 

Repository files navigation

qPlate

qPlate is the primary templating engine. It is an XML templating engine and used mostly to generate HTML fragments and pages. Can be used server side with nodejs or client side.

Template directives are specified as XML attributes prefixed with t- by default (you can choose it), for instance t-if for conditionals, with elements and other attributes being rendered directly.

To avoid element rendering, a placeholder element <t> is also available, which executes its directive but doesn't generate any output in and of itself:

<t t-if="condition">
    <p>Test</p>
</t>

will result in:

<p>Test</p>

if condition is true, but:

<div t-if="condition">
    <p>Test</p>
</div>

will result in:

<div>
    <p>Test</p>
</div>

data output

qPlate has a primary output directive which automatically HTML-escape its content limiting XSS risks when displaying user-provided content: esc.

esc takes an expression, evaluates it and prints the content:

<p><t t-esc="value"/></p>

rendered with the value value set to 42 yields:

<p>42</p>

There is one other output directive raw which behaves the same as respectively esc but does not HTML-escape its output. It can be useful to display separately constructed markup (e.g. from functions) or already sanitized user-provided markup.

conditionals

qPlate has a conditional directive if, which evaluates an expression given as attribute value:

<div>
    <t t-if="condition">
        <p>ok</p>
    </t>
</div>

The element is rendered if the condition is true:

<div>
    <p>ok</p>
</div>

but if the condition is false it is removed from the result:

<div>
</div>

The conditional rendering applies to the bearer of the directive, which does not have to be <t>:

<div>
    <p t-if="condition">ok</p>
</div>

will give the same results as the previous example.

loops

qPlate has an iteration directive foreach which take an expression returning the collection to iterate on, and a second parameter t-as providing the name to use for the "current item" of the iteration:

<t t-foreach="[1, 2, 3]" t-as="i">
    <p><t t-esc="i"/></p>
</t>

will be rendered as:

<p>1</p>
<p>2</p>
<p>3</p>

Like conditions, foreach applies to the element bearing the directive's attribute, and

<p t-foreach="[1, 2, 3]" t-as="i">
    <t t-esc="i"/>
</p>

is equivalent to the previous example.

foreach can iterate on an array (the current item will be the current value), a mapping (the current item will be the current key) or an integer (equivalent to iterating on an array between 0 inclusive and the provided integer exclusive).

In addition to the name passed via t-as, foreach provides a few other variables for various data points:

Warning

$as will be replaced by the name passed to t-as

$as_all
the object being iterated over
$as_value
the current iteration value, identical to $as for lists and integers, but for mappings it provides the value (where $as provides the key)
$as_index
the current iteration index (the first item of the iteration has index 0)
$as_size
the size of the collection if it is available
$as_first
whether the current item is the first of the iteration (equivalent to $as_index == 0)
$as_last
whether the current item is the last of the iteration (equivalent to $as_index + 1 == $as_size), requires the iteratee's size be available
$as_parity
either "even" or "odd", the parity of the current iteration round
$as_even
a boolean flag indicating that the current iteration round is on an even index
$as_odd
a boolean flag indicating that the current iteration round is on an odd index

These extra variables provided and all new variables created into the foreach are only available in the scope of the``foreach``. If the variable exists outside the context of the foreach, the value is copied at the end of the foreach into the global context.

<t t-set="existing_variable" t-value="False"/>
<!-- existing_variable now False -->

<p t-foreach="[1, 2, 3]" t-as="i"> <t t-set="existing_variable" t-value="True"/> <t t-set="new_variable" t-value="True"/> <!-- existing_variable and new_variable now True --> </p>

<!-- existing_variable always True --> <!-- new_variable undefined -->

attributes

qPlate can compute attributes on-the-fly and set the result of the computation on the output node. This is done via the t-att (attribute) directive which exists in 4 different forms:

$name="{{...}}"

an attribute called $name is created, the attribute value contains in the braces is evaluated and the result is set as the attribute's value:

<div class="static {{'add-class'}}"/>

will be rendered as:

<div class="static add-class"></div>
t-att-$name

an attribute called $name is created, the attribute value is evaluated and the result is set as the attribute's value:

<div t-att-a="42"/>

will be rendered as:

<div a="42"></div>
t-attf-$name

same as previous, but the parameter is a format string instead of just an expression, often useful to mix literal and non-literal string (e.g. classes):

<t t-foreach="[1, 2, 3]" t-as="item">
<li t-attf-class="row {{ item_parity }}"><t t-esc="item"/></li>
</t>

will be rendered as:

<li class="row even">1</li>
<li class="row odd">2</li>
<li class="row even">3</li>
t-att=mapping

if the parameter is a mapping, each (key, value) pair generates a new attribute and its value:

<div t-att="{'a': 1, 'b': 2}"/>

will be rendered as:

<div a="1" b="2"></div>
t-att=pair

if the parameter is a pair (tuple or array of 2 element), the first item of the pair is the name of the attribute and the second item is the value:

<div t-att="['a', 'b']"/>

will be rendered as:

<div a="b"></div>

setting variables

qPlate allows creating variables from within the template, to memoize a computation (to use it multiple times), give a piece of data a clearer name, ...

This is done via the set directive, which takes the name of the variable to create. The value to set can be provided in two ways:

  • a t-value attribute containing an expression, and the result of its evaluation will be set:

    <t t-set="foo" t-value="2 + 1"/>
    <t t-esc="foo"/>
    

    will print 3

  • if there is no t-value attribute, the node's body is rendered and set as the variable's value:

    <t t-set="foo">
    <li>ok</li>
    </t>
    <t t-esc="foo"/>
    

    will generate &lt;li&gt;ok&lt;/li&gt; (the content is escaped as we used the esc directive)

    Note

    using the result of this operation is a significant use-case for the raw directive.

calling sub-templates

qPlate templates can be used for top-level rendering, but they can also be used from within another template (to avoid duplication or give names to parts of templates) using the t-call directive:

<t t-call="other-template"/>

This calls the named template with the execution context of the parent, if other_template is defined as:

<p><t t-value="var"/></p>

the call above will be rendered as <p/> (no content), but:

<t t-set="var" t-value="1"/>
<t t-call="other-template"/>

will be rendered as <p>1</p>.

However this has the problem of being visible from outside the t-call. Alternatively, content set in the body of the call directive will be evaluated before calling the sub-template, and can alter a local context:

<t t-call="other-template">
<t t-set="var" t-value="1"/>
</t>
<!-- "var" does not exist here -->

The body of the call directive can be arbitrarily complex (not just set directives), and its rendered form will be available within the called template as a magical 0 variable:

<div>
This template was called with content:
<t t-raw="0"/>
</div>

being called thus:

<t t-call="other-template">
<em>content</em>
</t>

will result in:

<div>
This template was called with content:
<em>content</em>
</div>

template inheritance

Template inheritance is used to alter existing templates in-place, e.g. to add information to templates created by an other modules.

Template inheritance is performed via the t-extend directive which takes the name of the template to alter as parameter.

The alteration is then performed with any number of t-xpath sub-directives:

<t t-extend="base.template">
<t t-xpath="//ul" t-operation="append">
<li>new element</li>
</t>
<t t-xpath="//ul/li/@name" t-operation="replace">attribute value</t>
</t>

The t-xpath directives takes a Xpath selector. This selector is used on the extended template to select context nodes to which the specified t-operation is applied:

append
the node's body is appended at the end of the context node (after the context node's last child)
prepend
the node's body is prepended to the context node (inserted before the context node's first child)
replace
the node's body is used to replace the context node itself
before
the node's body is inserted right before the context node (not available when target an attribute)
after
the node's body is inserted right after the context node (not available when target an attribute)
inner
the node's body replaces the context node's children (not available when target an attribute)
No operation

if no t-operation is specified, the template body is interpreted as javascript code and executed with the context node as this

Warning

while much more powerful than other operations, this mode is also much harder to debug and maintain, it is recommended to avoid it

debugging

The javascript qPlate implementation provides a few debugging hooks:

t-log

takes an expression parameter, evaluates the expression during rendering and logs its result with console.log:

<t t-set="foo" t-value="42"/>
<t t-log="foo"/>

will print 42 to the console

t-debug

triggers a debugger breakpoint during template rendering:

<t t-if="a_test">
<t t-debug="">
</t>

will stop execution if debugging is active (exact condition depend on the browser and its development tools)

t-js

the node's body is javascript code executed during template rendering. Takes a context parameter, which is the name under which the rendering context will be available in the t-js's body:

<t t-set="foo" t-value="42"/>
<t t-js="ctx">
console.log("Foo is", ctx.foo);
</t>

XPath

See also http://jean-luc.massat.perso.luminy.univ-amu.fr/ens/xml/04-xpath.html
Expression Refers to

./author

All <author> elements within the current context. Note that this is equivalent to the expression in the next row.

author

All <author> elements within the current context.

first.name

All <first.name> elements within the current context.

/bookstore

The document element (<bookstore>) of this document.

//author

All <author> elements in the document.

book[/bookstore/@specialty=@style]

All <book> elements whose style attribute value is equal to the specialty attribute value of the <bookstore> element at the root of the document.

author/first-name

All <first-name> elements that are children of an <author> element.

bookstore//title

All <title> elements one or more levels deep in the <bookstore> element (arbitrary descendants). Note that this is different from the expression in the next row.

bookstore/*/title

All <title> elements that are grandchildren of <bookstore> elements.

bookstore//book/excerpt//emph

All <emph> elements anywhere inside <excerpt> children of <book> elements, anywhere inside the <bookstore> element.

.//title

All <title> elements one or more levels deep in the current context. Note that this situation is essentially the only one in which the period notation is required.

author/*

All elements that are the children of <author> elements.

book/*/last-name

All <last-name> elements that are grandchildren of <book> elements.

*/*

All grandchildren elements of the current context.

*[@specialty]

All elements with the specialty attribute.

@style

The style attribute of the current context.

price/@exchange

The exchange attribute on <price> elements within the current context.

price/@exchange/total

Returns an empty node set, because attributes do not contain element children. This expression is allowed by the XML Path Language (XPath) grammar, but is not strictly valid.

book[@style]

All <book> elements with style attributes, of the current context.

book/@style

The style attribute for all <book> elements of the current context.

@*

All attributes of the current element context.

./first-name

All <first-name> elements in the current context node. Note that this is equivalent to the expression in the next row.

first-name

All <first-name> elements in the current context node.

author[1]

The first <author> element in the current context node.

author[first-name][3]

The third <author> element that has a <first-name> child.

my:book

The <book> element from the my namespace.

my:*

All elements from the my namespace.

@my:*

All attributes from the my namespace (this does not include unqualified attributes on elements from the my namespace).

------------

x/y[1]

The first <y> child of each <x>. This is equivalent to the expression in the next row.

x/y[position() = 1]

The first <y> child of each <x>.

(x/y)[1]

The first <y> from the entire set of <y> children of <x> elements.

x[1]/y[2]

The second <y> child of the first <x>.

------------

book[last()]

The last <book> element of the current context node.

book/author[last()]

The last <author> child of each <book> element of the current context node.

(book/author)[last()]

The last <author> element from the entire set of <author> children of <book> elements of the current context node.

book[excerpt]

All <book> elements that contain at least one <excerpt> element child.

book[excerpt]/title

All <title> elements that are children of <book> elements that also contain at least one <excerpt> element child.

book[excerpt]/author[degree]

All <author> elements that contain at least one <degree> element child, and that are children of <book> elements that also contain at least one <excerpt> element.

book[author/degree]

All <book> elements that contain <author> children that in turn contain at least one <degree> child.

author[degree][award]

All <author> elements that contain at least one <degree> element child and at least one <award> element child.

author[degree and award]

All <author> elements that contain at least one <degree> element child and at least one <award> element child.

author[(degree or award) and publication]

All <author> elements that contain at least one <degree> or <award> and at least one <publication> as the children

author[degree and not(publication)]

All <author> elements that contain at least one <degree> element child and that contain no <publication> element children.

author[not(degree or award) and publication]

All <author> elements that contain at least one <publication> element child and contain neither <degree> nor <award> element children.

author[last-name = "Bob"]

All <author> elements that contain at least one <last-name> element child with the value Bob.

author[last-name[1] = "Bob"]

All <author> elements where the first <last-name> child element has the value Bob. Note that this is equivalent to the expression in the next row.

author[last-name [position()=1]= "Bob"]

All <author> elements where the first <last-name> child element has the value Bob.

degree[@from != "Harvard"]

All <degree> elements where the from attribute is not equal to "Harvard".

degree[@from %= "Har%"]

All <degree> elements where the from attribute begin by "Har".

degree[@from *= "arva"]

All <degree> elements where the from attribute contains "arva".

degree[@from ?= "[Aar]+.a"]

All <degree> elements where the from attribute match as regular expression "[Aar]+.a".

author[. = "Matthew Bob"]

All <author> elements whose value is Matthew Bob.

author[last-name = "Bob" and ../price &gt; 50]

All <author> elements that contain a <last-name> child element whose value is Bob, and a <price> sibling element whose value is greater than 50.

book[position() &lt;= 3]

The first three books (1, 2, 3).

author[not(last-name = "Bob")]

All <author> elements that do no contain <last-name> child elements with the value Bob.

author[first-name = "Bob"]

All <author> elements that have at least one <first-name> child with the value Bob.

author[* = "Bob"]

all author elements containing any child element whose value is Bob.

author[last-name = "Bob" and first-name = "Joe"]

All <author> elements that has a <last-name> child element with the value Bob and a <first-name> child element with the value Joe.

price[@intl = "Canada"]

All <price> elements in the context node which have an intl attribute equal to "Canada".

degree[position() &lt; 3]

The first two <degree> elements that are children of the context node.

p/text()[2]

The second text node in each <p> element in the context node.

ancestor::book[1]

The nearest <book> ancestor of the context node.

ancestor::book[author][1]

The nearest <book> ancestor of the context node and this <book> element has an <author> element as its child.

ancestor::author[parent::book][1]

The nearest <author> ancestor in the current context and this <author> element is a child of a <book> element.

About

XML/HTML templating engine (for browsers & nodejs)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published