2019-04-24 11:54:26 +02:00
# 🦉 QWeb 🦉
2019-03-14 12:51:19 +01:00
2019-04-25 17:24:39 +02:00
## Content
2019-03-14 14:23:27 +01:00
2019-04-25 17:24:39 +02:00
- [Overview ](#overview )
2019-06-04 13:16:50 +02:00
- [Directives ](#directives )
2019-05-15 10:55:39 +02:00
- [QWeb Engine ](#qweb-engine )
2019-06-07 14:31:22 +02:00
- [Reference ](#reference )
2019-06-14 15:24:04 +02:00
- [White Spaces ](#white-spaces )
- [Root Nodes ](#root-nodes )
- [Expression Evaluation ](#expression-evaluation )
- [Static html Nodes ](#static-html-nodes )
- [Outputting Data ](#outputting-data )
2019-06-04 13:16:50 +02:00
- [Setting Variables ](#setting-variables )
- [Conditionals ](#conditionals )
2019-06-14 15:24:04 +02:00
- [Dynamic Attributes ](#dynamic-attributes )
2019-06-04 13:16:50 +02:00
- [Loops ](#loops )
- [Rendering Sub Templates ](#rendering-sub-templates )
- [Debugging ](#debugging )
2019-03-14 14:23:27 +01:00
2019-04-25 17:24:39 +02:00
## Overview
2019-03-14 14:23:27 +01:00
2019-10-18 09:22:28 +02:00
[QWeb ](https://www.odoo.com/documentation/13.0/reference/qweb.html ) is the primary templating engine used by Odoo. It is based on the XML format, and used
2019-06-04 13:16:50 +02:00
mostly to generate HTML. In OWL, QWeb templates are compiled into functions that
generate a virtual dom representation of the HTML.
2019-03-14 14:23:27 +01:00
2019-04-25 17:24:39 +02:00
Template directives are specified as XML attributes prefixed with `t-` , for instance `t-if` for conditionals, with elements and other attributes being rendered directly.
2019-03-14 14:23:27 +01:00
2019-04-25 17:24:39 +02:00
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.
2019-03-22 11:09:57 +01:00
2019-06-04 13:16:50 +02:00
``` xml
<div >
<span t-if= "somecondition" > Some string</span>
<ul t-else= "1" >
<li t-foreach= "messages" t-as= "message" >
2019-10-18 09:22:28 +02:00
<t t-esc= "message" />
2019-06-04 13:16:50 +02:00
</li>
</ul>
</div>
```
The QWeb class in the OWL project is an implementation of that specification
with a few interesting points:
- it compiles templates into functions that output a virtual DOM instead of a
string. This is necessary for the component system.
2019-06-20 09:42:33 +02:00
- it has a few extra directives: `t-component` , `t-on` , ...
2019-06-04 13:16:50 +02:00
## Directives
We present here a list of all standard QWeb directives:
| Name | Description |
| ------------------------------ | ------------------------------------------------------------ |
| `t-esc` | [Outputting safely a value ](#outputting-data ) |
| `t-raw` | [Outputting value, without escaping ](#outputting-data ) |
| `t-set` , `t-value` | [Setting variables ](#setting-variables ) |
| `t-if` , `t-elif` , `t-else` , | [conditionally rendering ](#conditionals ) |
| `t-foreach` , `t-as` | [Loops ](#loops ) |
| `t-att` , `t-attf-*` , `t-att-*` | [Dynamic attributes ](#dynamic-attributes ) |
| `t-call` | [Rendering sub templates ](#rendering-sub-templates ) |
| `t-debug` , `t-log` | [Debugging ](#debugging ) |
| `t-name` | [Defining a template (not really a directive) ](#qweb-engine ) |
The component system in Owl requires additional directives, to express various
needs. Here is a list of all Owl specific directives:
2019-09-11 14:01:49 +02:00
| Name | Description |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| `t-component` , `t-props` , `t-keepalive` , `t-asyncroot` | [Defining a sub component ](component.md#composition ) |
| `t-ref` | [Setting a reference to a dom node or a sub component ](component.md#references ) |
| `t-key` | [Defining a key (to help virtual dom reconciliation) ](component.md#t-key-directive ) |
| `t-on-*` | [Event handling ](component.md#event-handling ) |
| `t-transition` | [Defining an animation ](animations.md#css-transitions ) |
| `t-slot` | [Rendering a slot ](component.md#slots ) |
| `t-model` | [Form input bindings ](component.md#form-input-bindings ) |
2019-03-22 11:09:57 +01:00
2019-05-15 10:55:39 +02:00
## QWeb Engine
This section is about the javascript code that implements the `QWeb` specification.
2019-05-16 10:23:18 +02:00
Owl exports a `QWeb` class in `owl.QWeb` . To use it, it just needs to be
2019-05-15 10:55:39 +02:00
instantiated:
``` js
const qweb = new owl . QWeb ( ) ;
```
2019-10-18 09:22:28 +02:00
Its API is quite simple:
2019-05-15 10:55:39 +02:00
- **`constructor(data)` **: constructor. Takes an optional string to add initial
templates (see `addTemplates` for more information on format of the string).
```js
const qweb = new owl.QWeb(TEMPLATES);
` ``
2019-06-24 22:41:02 +02:00
- **` addTemplate(name, xmlStr, allowDuplicate)`**: add a specific template.
2019-05-15 10:55:39 +02:00
` ``js
2019-05-16 10:23:18 +02:00
qweb.addTemplate("mytemplate", "<div>hello</div>");
2019-05-15 10:55:39 +02:00
` ``
2019-10-18 09:22:28 +02:00
If the optional ` allowDuplicate` is set to ` true`, then ` QWeb` will simply
ignore templates added for a second time. Otherwise, ` QWeb` will crash.
2019-06-24 22:41:02 +02:00
2019-05-15 10:55:39 +02:00
- **` addTemplates(xmlStr)`**: add a list of templates (identified by ` t-name`
attribute).
` ``js
const TEMPLATES = `
<templates>
<div t-name="App" class="main">main</div>
2019-06-20 09:42:33 +02:00
<div t-name="OtherComponent">other component</div>
2019-05-15 10:55:39 +02:00
</templates>`;
qweb.addTemplates(TEMPLATES);
` ``
2019-05-16 10:23:18 +02:00
- **` render(name, context, extra)`**: renders a template. This returns a ` vnode`,
2019-05-15 10:55:39 +02:00
which is a virtual representation of the DOM (see [vdom doc](vdom.md)).
` ``js
2019-06-20 09:42:33 +02:00
const vnode = qweb.render("App", component);
2019-05-15 10:55:39 +02:00
` ``
2019-07-09 09:45:43 +02:00
- **` renderToString(name, context)`**: renders a template, but returns an html
string.
` ``js
const str = qweb.renderToString("someTemplate", somecontext);
` ``
2019-10-18 09:22:28 +02:00
- **` registerTemplate(name, template)`**: static function to register a global
2019-09-12 10:38:53 +02:00
QWeb template. This is useful for commonly used components accross the
application, and for making a template available to an application without
having a reference to the actual QWeb instance.
` ``js
2019-10-18 09:22:28 +02:00
QWeb.registerTemplate("mytemplate", ` <div>some template</div>`);
2019-09-12 10:38:53 +02:00
` ``
2019-09-12 10:45:40 +02:00
- **` registerComponent(name, Component)`**: static function to register an OWL Component
2019-05-23 16:14:52 +02:00
to QWeb's global registry. Globally registered Components can be used in
2019-06-20 09:42:33 +02:00
templates (see the ` t-component` directive). This is useful for commonly used
2019-05-23 16:14:52 +02:00
components accross the application.
` ``js
class Dialog extends owl.Component { ... }
2019-09-12 10:45:40 +02:00
QWeb.registerComponent("Dialog", Dialog);
2019-05-23 16:14:52 +02:00
...
2019-06-20 09:42:33 +02:00
class ParentComponent extends owl.Component { ... }
qweb.addTemplate("ParentComponent", "<div><Dialog/></div>");
2019-05-23 16:14:52 +02:00
` ``
2019-06-22 14:44:09 +02:00
In some way, a ` QWeb` instance is the core of an Owl application. It is the only
mandatory element of an [environment](component.md#environment). As such, it
2019-10-18 09:22:28 +02:00
has an extra responsibility: it can act as an event bus for internal communication
2019-06-22 14:44:09 +02:00
between Owl classes. This is the reason why ` QWeb` actually extends [EventBus](event_bus.md).
2019-06-04 13:16:50 +02:00
## Reference
2019-04-25 17:24:39 +02:00
2019-05-15 10:55:39 +02:00
We define in this section the specification of how ` QWeb` templates should be
2019-06-04 13:16:50 +02:00
rendered. Note that we only document here the standard QWeb specification. Owl
specific extensions are documented in various other parts of the documentation.
2019-06-14 15:24:04 +02:00
### White Spaces
2019-06-04 13:16:50 +02:00
2019-10-18 09:22:28 +02:00
White spaces in a template are handled in a special way:
2019-06-04 13:16:50 +02:00
- consecutive whitespaces are always condensed to a single whitespace
- if a whitespace-only text node contains a linebreak, it is ignored
- the previous rules do not apply if we are in a ` <pre>` tag
2019-06-14 15:24:04 +02:00
### Root Nodes
2019-06-04 13:16:50 +02:00
For many reasons, Owl QWeb templates should have a single root node. More
precisely, the result of a template rendering should have a single root node:
` ``xml
<!–– not ok: two root nodes ––>
<t>
<div>foo</div>
<div>bar</div>
</t>
<!–– ok: result has one single root node ––>
<t>
<div t-if="someCondition">foo</div>
<span t-else="1">bar</span>
</t>
` ``
Extra root nodes will actually be ignored (even though they will be rendered
in memory).
Note: this does not apply to subtemplates (see the ` t-call` directive). In that
case, they will be inlined in the main template, and can actually have many
root nodes.
2019-06-14 15:24:04 +02:00
### Expression Evaluation
2019-06-04 13:16:50 +02:00
2019-06-04 16:21:31 +02:00
QWeb expressions are strings that will be processed at compile time. Each variable in
the javascript expression will be replaced by a lookup in the context (so, the
2019-06-20 09:42:33 +02:00
component). For example, ` a + b.c(d)` will be converted into:
2019-06-04 16:21:31 +02:00
` ``js
2019-06-11 15:02:10 +02:00
context["a"] + context["b"].c(context["d"]);
2019-06-04 16:21:31 +02:00
` ``
2019-10-18 09:22:28 +02:00
It is useful to explain the various rules that apply on these expressions:
2019-06-04 13:16:50 +02:00
1. it should be a simple expression which returns a value. It cannot be a statement.
` ``xml
<div><p t-if="1 + 2 === 3">ok</p></div>
` ``
is valid, but the following is not valid:
` ``xml
<div><p t-if="console.log(1)">NOT valid</p></div>
` ``
2019-06-04 16:21:31 +02:00
2. it can use anything in the rendering context (typically, the component):
2019-06-04 13:16:50 +02:00
` ``xml
2019-06-04 16:21:31 +02:00
<p t-if="user.birthday === today()">Happy bithday!</p>
2019-06-04 13:16:50 +02:00
` ``
is valid, and will read the ` user` object from the context, and call the
` today` function.
3. it can use a few special operators to avoid using symbols such as ` <`, ` >`,
` &` or ` |`. This is useful to make sure that we still write valid XML.
2019-05-01 10:47:24 +02:00
2019-06-04 13:16:50 +02:00
| Word | will be replaced by |
| ----- | ------------------- |
| ` and` | ` &&` |
| ` or` | ` \|\|` |
| ` gt` | ` >` |
| ` gte` | ` >=` |
| ` lt` | ` <` |
| ` lte` | ` <=` |
So, one can write this:
` ``xml
<div><p t-if="10 + 2 gt 5">ok</p></div>
` ``
### Static Html Nodes
2019-04-27 11:55:43 +02:00
Normal, regular html nodes are rendered into themselves:
` ``xml
<div>hello</div> <!–– rendered as itself ––>
` ``
2019-06-04 13:16:50 +02:00
### Outputting Data
2019-04-27 11:55:43 +02:00
The ` t-esc` directive is necessary whenever you want to add a dynamic text
expression in a template. The text is escaped to avoid security issues.
` ``xml
<p><t t-esc="value"/></p>
` ``
rendered with the value ` value` set to ` 42` in the rendering context yields:
` ``html
<p>42</p>
` ``
The ` t-raw` directive is almost the same as ` t-esc`, but without the escaping.
This is mostly useful to inject a raw html string somewhere. Obviously, this
is unsafe to do in general, and should only be used for strings known to be safe.
` ``xml
2019-06-04 13:16:50 +02:00
<p><t t-raw="value"/></p>
2019-04-27 11:55:43 +02:00
` ``
rendered with the value ` value` set to ` <span>foo</span>` in the rendering context yields:
` ``html
<p><span>foo</span></p>
` ``
2019-10-12 09:07:25 +02:00
Note that since the content of the expression is not known beforehand, the ` t-raw`
directive has to parse the html (and convert it to a virtual dom structure) for
each rendering. So, it will be much slower than a regular template. It is
therefore advised to limit the use of ` t-raw` whenever possible.
2019-06-04 13:16:50 +02:00
### Setting Variables
2019-04-27 11:55:43 +02:00
QWeb 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 ` t-set` directive, which takes the name of the variable to create. The value to set can be provided in two ways:
1. a ` t-value` attribute containing an expression, and the result of its
evaluation will be set:
` ``xml
<t t-set="foo" t-value="2 + 1"/>
<t t-esc="foo"/>
` ``
will print ` 3`. Note that the evaluation is done at rendering time, not at
compilte time.
2. if there is no ` t-value` attribute, the node’ s body is saved and its value is
set as the variable’ s value:
` ``xml
<t t-set="foo">
<li>ok</li>
</t>
<t t-esc="foo"/>
` ``
will generate ` <li>ok</li>` (the content is escaped as we used the ` t-esc` directive)
The ` t-set` directive acts like a regular variable in most programming language.
It is lexically scoped (inner nodes are sub scopes), can be shadowed, ...
2019-06-04 13:16:50 +02:00
### Conditionals
2019-04-27 11:55:43 +02:00
The ` t-if` directive is useful to conditionally render something. It evaluates
the expression given as attribute value, and then acts accordingly.
` ``xml
<div>
<t t-if="condition">
<p>ok</p>
</t>
</div>
` ``
The element is rendered if the condition (evaluated with the current rendering
context) is true:
` ``xml
<div>
<p>ok</p>
</div>
` ``
but if the condition is false it is removed from the result:
` ``xml
<div>
</div>
` ``
The conditional rendering applies to the bearer of the directive, which does not
have to be ` <t>`:
` ``xml
<div>
<p t-if="condition">ok</p>
</div>
` ``
will give the same results as the previous example.
Extra conditional branching directives ` t-elif` and ` t-else` are also available:
` ``xml
<div>
<p t-if="user.birthday == today()">Happy bithday!</p>
<p t-elif="user.login == 'root'">Welcome master!</p>
<p t-else="">Welcome!</p>
</div>
` ``
2019-06-14 15:24:04 +02:00
### Dynamic Attributes
2019-04-27 11:55:43 +02:00
2019-04-29 14:15:19 +02:00
One can use the ` t-att-` directive to add dynamic attributes. Its main use is to
evaluate an expression (at rendering time) and bind an attribute to its result:
2019-04-25 17:24:39 +02:00
2019-04-29 14:15:19 +02:00
For example, if we have ` id` set to 32 in the rendering context,
` ``xml
2019-05-17 16:14:54 +02:00
<div t-att-data-action-id="id"/> <!-- result: <div data-action-id="32"></div> -->
2019-04-29 14:15:19 +02:00
` ``
If an expression evaluates to a falsy value, it will not be set at all:
` ``xml
<div t-att-foo="false"/> <!-- result: <div></div> -->
` ``
2019-04-25 17:24:39 +02:00
2019-05-17 16:14:54 +02:00
There is another way to format a string attribute: the ` t-attf-` directive. With
it, you get string interpolation:
` ``xml
2019-05-28 13:17:38 +02:00
<div t-attf-foo="a {{value1}} is {{value2}} of {{value3}} ]"/>
2019-05-17 16:14:54 +02:00
<!-- result if values are set to 1,2 and 3: <div foo="a 0 is 1 of 2 ]"></div> -->
` ``
2019-06-04 13:16:50 +02:00
### Loops
QWeb has an iteration directive ` t-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:
` ``xml
<t t-foreach="[1, 2, 3]" t-as="i">
<p><t t-esc="i"/></p>
</t>
` ``
will be rendered as:
` ``xml
<p>1</p>
<p>2</p>
<p>3</p>
` ``
Like conditions, ` t-foreach` applies to the element bearing the directive’ s attribute, and
` ``xml
<p t-foreach="[1, 2, 3]" t-as="i">
<t t-esc="i"/>
</p>
` ``
is equivalent to the previous example.
2019-06-07 14:10:59 +02:00
` t-foreach` can iterate on an array (the current item will be the current value)
or an object (the current item will be the current key).
2019-06-04 13:16:50 +02:00
In addition to the name passed via t-as, ` t-foreach` provides a few other
variables for various data points (note: ` $as` will be replaced by the name
passed to ` t-as`):
- ` $as_value`: the current iteration value, identical to ` $as` for lists and
integers, but for objects, 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_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
These extra variables provided and all new variables created into the ` t-foreach`
are only available in the scope of the ` t-foreach`. If the variable exists outside
the context of the ` t-foreach`, the value is copied at the end of the foreach
into the global context.
` ``xml
<t t-set="existing_variable" t-value="False"/>
<!-- existing_variable now False -->
2019-06-07 14:10:59 +02:00
<p t-foreach="Array(3)" t-as="i">
2019-06-04 13:16:50 +02:00
<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 -->
` ``
### Rendering Sub Templates
2019-05-03 16:01:59 +02:00
QWeb 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:
` ``xml
<div t-name="other-template">
<p><t t-value="var"/></p>
</div>
<div t-name="main-template">
<t t-set="var" t-value="owl"/>
<t t-call="other-template"/>
</div>
` ``
2019-05-16 10:23:18 +02:00
will be rendered as ` <div><p>owl</p></div>`. This example shows that the sub
template is rendered with the execution context of the parent. The sub template
2019-05-03 16:01:59 +02:00
is actually inlined in the main template, but in a sub scope: variables defined
in the sub template do not escape.
2019-05-16 10:23:18 +02:00
Sometimes, one might want to pass information to the sub template. In that case,
2019-05-03 16:01:59 +02:00
the content of the body of the ` t-call` directive is available as a special
magic variable ` 0`:
` ``xml
<t t-name="other-template">
This template was called with content:
<t t-raw="0"/>
</t>
<div t-name="main-template">
<t t-call="other-template">
<em>content</em>
</t>
</div>
` ``
will result in :
` ``xml
<div>
This template was called with content:
<em>content</em>
</div>
` ``
2019-06-04 13:16:50 +02:00
### Debugging
2019-04-29 13:33:46 +02:00
The javascript QWeb implementation provides two useful debugging directives:
` t-debug` adds a debugger statement during template rendering:
` ``xml
<t t-if="a_test">
<t t-debug="">
</t>
2019-05-16 10:23:18 +02:00
` ``
2019-04-29 13:33:46 +02:00
will stop execution if the browser dev tools are open.
` t-log` takes an expression parameter, evaluates the expression during rendering and logs its result with console.log:
` ``xml
<t t-set="foo" t-value="42"/>
<t t-log="foo"/>
` ``
2019-10-18 09:22:28 +02:00
will print 42 to the console.