The QWeb template language
The QWeb parser looks for special directives in the templates and replaces them with dynamically generated HTML. These directives are XML element attributes and can be used in any valid tag or element, such as
<div>, <span>, or <field>.
Sometimes, we may want to use a QWeb directive but we don't want to place it in any of the XML elements in our template. For those cases, we have a
<t> special element that can have QWeb directives, such as t-if or t-foreach, but is silent and won't have any output on the final XML/HTML produced.
The QWeb directives will frequently make use of evaluated expressions to produce different results, depending on the current record values. There are two different QWeb implementations: client-side JavaScript and server-side Python.
The reports and website pages use the server-side Python implementation of QWeb.
Kanban views use the client-side JavaScript implementation. This means that the QWeb expression used in Kanban views should be written using the JavaScript syntax, not Python.
When displaying a Kanban view, the internal steps are roughly as follows:
- Get the XML for the templates to render.
- Call the server
read()method to get the data for the fields mentioned in the templates. - Locate the
kanban-boxtemplate and parse it using QWeb to output the final HTML fragments. - Inject the HTML in the browser display (the DOM).
This is not meant to be technically exact. It's just a mind map that can be useful to understand how things work in Kanban views.
Next, we'll learn about QWeb expression evaluation and explore the available QWeb directives, using examples that enhance the checkout Kanban card.
Many of the QWeb directives use expressions that are evaluated to produce some result. When used from the client side, as is the case for Kanban views, these expressions are written in JavaScript. They're evaluated in a context that has a few useful variables available.
A
record object is available, representing the current record, with the fields requested from the server. The field values can be accessed using either the raw_value or value attributes:raw_valueis the value returned by theread()server method, so it's more suitable for use in condition expressions.valueis formatted according to the user settings and is meant to be used for display in the user interface. This is typically relevant for date/datetime, float/monetary, and relational fields.
The QWeb evaluation context also has references available for the JavaScript web client instance. To make use of them, a good understanding of the web client architecture is needed, but we won't be able to go into that in detail. For reference purposes, the following identifiers are available in QWeb expression evaluation:
widgetis a reference to the currentKanbanRecord()widget object, responsible for the rendering of the current record into a Kanban card. It exposes some helper functions we can use.recordis a shortcut forwidget.recordand provides access to the fields available, using dot notation.read_only_modeindicates whether the current view is in read mode (and not in edit mode). It's a shortcut forwidget.view.options.read_only_mode.instanceis a reference to the full web client instance.
It's also noteworthy that some characters are not allowed inside expressions. The lower than sign (
<) is such a case. This is because of the XML standard, where such characters have special meaning and shouldn't be used on the XML content. A negated >= operator is a valid alternative, but the common practice is to use the following alternative symbols that are available for inequality operations:ltis for less than.lteis for less than or equal to.gtis for greater than.gteis for greater than or equal to.
Our Kanban card is using the
t-attf QWeb directive to dynamically set a class on the top <div> element so that the card is colored depending on the color field value. For this, the t-attf- QWeb directive was used.
The
t-attf- directive dynamically generates tag attributes using string substitution. This allows for parts of larger strings generated dynamically, such as a URL address or CSS class names.
The directive looks for expression blocks that will be evaluated and replaced by the results. These are delimited either by
{{ and }} or by #{ and }. The content of the blocks can be any valid JavaScript expression and can use any of the variables available for QWeb expressions, such as record and widget.
In our case, we also used the
kanban_color() JavaScript function, specially provided to map color index numbers into the CSS class color names.
As an elaborate example, we'll use this directive to dynamically change the color of the user, to be in red font if the priority is high. For this, replace
<field name="user_id"/> in our Kanban card with the following:<li t-attf-class="oe_kanban_text_{{
record.user_id.raw_value lt '2'
? 'black' : 'red' }}">
<field name="user_id"/>
</li>
This results in either
class="oe_kanban_text_red" or class="oe_kanban_text_black", depending on the checkout's priority value. Please note that, while the oe_kanban_text_red CSS class is available in Kanban views, the oe_kanban_text_black CSS class does not exist and was used to explain the point.
The
t-att- QWeb directive dynamically generates an attribute value by evaluating an expression.
Our Kanban card uses it to dynamically set some attributes on the
<img> tag; the title attribute is dynamically rendered using the following:t-att-title="record.member_id.value"
The
.value field returns its value representation as it should be shown on the screen. For many-to-one fields, this is usually the related record's name value. For users, this is the username. As a result, when hovering the mouse pointer over the image, you will see the corresponding username.
When the expression evaluates to a false equivalent value, the attribute is not rendered at all. This is important for special HTML attributes such as the
checked input field, which can have an effect even without an attribute value.
A block of HTML can be repeated by iterating through a loop. We can use it to add the avatars of the record followers.
Let's start by rendering just the partner IDs of the record, as follows:
<t t-foreach="record.message_partner_ids.raw_value" t-as="rec">
<t t-esc="rec" />;
</t>
The
t-foreach directive accepts a JavaScript expression evaluating to a collection to iterate. In most cases, this will be just the name of a to-many relation field. It's used with a t-as directive to set the name to be used to refer to each item in the iteration.
The
t-esc directive used next evaluates the provided expression, just the rec variable name in this case, and renders it as safely escaped HTML.
In the previous example, we loop through the followers stored in the
message_parter_ids field. Since there is limited space on the Kanban card, we could have used the slice() JavaScript function to limit the number of followers to display, as shown in the following:t-foreach="record.message_partner_ids.raw_value.slice(0, 3)"
The
rec variable holds each iteration value, a partner ID in this case. With this, we can rewrite the follower loop as follows:<t t-foreach="record.message_parter_ids.raw_value.slice(0, 3)"
t-as="rec">
<img t-att-src="kanban_image('res.partner', 'image_small', rec)"
class="oe_avatar" width="24" height="24" />
</t>
For example, this could be added next to the responsible user image, in the right-hand footer.
A few helper variables are also available. Their name has the variable name defined in
t-as as a prefix. In our example, we used rec, so the helper variables available are as follows:rec_indexis the iteration index, starting from zerorec_sizeis the number of elements of the collectionrec_firstis true on the first element of the iterationrec_lastis true on the last element of the iterationrec_evenis true on even indexesrec_oddis true on odd indexesrec_parityis eitheroddoreven, depending on the current indexrec_allrepresents the object being iterated overrec_value, when iterating through a{key:value}dictionary, holds the value (recholds the key name)
<t t-foreach="record.message_parter_ids.raw_value.slice(0, 3)"
t-as="rec">
<t t-esc="rec" />
<t t-if="!rec_last">;</t>
</t>
Our Kanban view used the
t-if directive in the card option menu to make some options available depending on some conditions. The t-if directive expects an expression to be evaluated in JavaScript when rendering Kanban views on the client side. The tag and its content will be rendered only if the condition evaluates to true.
As an example, to only display the checkout's number of books borrowed if it has a value, add the following after the
request_date field:<t t-if="record.num_books.raw_value gt 0">
<li> <field name="num_books"/> books</li>
</t>
We used a
<t t-if="..."> element so that if the condition is false, the element produces no output. If it's true, only the contained <li> element is rendered to the output. Notice that the condition expression used the gt symbol instead of > to represent the greater than operator.
The
else if and else conditions are also supported with the t-elif and t-else directives. Here is an example of their usage:<t t-if="record.num_books.raw_value == 0">
<li>No books.</li>
</t>
<t t-elif="record.num_books.raw_value gt 9">
<li>A lot of books!</li>
</t>
<t t-else="">
<li> <field name="num_books"/> books.</li>
</t>
In Javascript expressions, the
AND and OR operators are && and ||. But the ampersand symbol is not allowed in XML. We can work around this using the and and or operators.
We used the
<field> element to render the field content. But field values can also be presented directly without a <field> tag.
The
t-esc directive evaluates an expression and renders it as an HTML-escaped value, as shown in the following:<t t-esc="record.message_parter_ids.raw_value" />
In some cases, and if the source data is guaranteed to be safe,
t-raw can be used to render the field raw value without any escaping, as shown in the following example:<t t-raw="record.message_parter_ids.raw_value" />
For more complex logic, we can store the result of an expression into a variable to use it later in the template. This is to be done using the
t-set directive, naming the variable to set followed by the t-value directive, with the expression calculating the value to assign.
As an example, the following code renders missed deadlines in red, just as in the previous section, but uses a
red_or_black variable for the CSS class to use, as shown in the following:<t t-set="red_or_black"
t-value="
record.priority.raw_value gte '2'
? 'oe_kanban_text_red' : ''" />
<li t-att-class="red_or_black">
<field name="user_id" />
</li>
Variables can also be assigned HTML content, as in the following example:
<t t-set="calendar_sign">
<i class="fa fa-calendar"/>
</t>
<t t-raw="calendar_sign" />
QWeb templates can be reusable HTML snippets that can be inserted into other templates. Instead of repeating the same HTML blocks over and over again, we can design building blocks to compose more complex user interface views.
Reusable templates are defined inside the
<templates> tag and identified by a top element with a t-nameother than kanban-box. These other templates can then be included using the t-call directive. This is true for the templates declared in the same Kanban view, somewhere else in the same add-on module or in a different add-on.
The follower avatar list is something that could be isolated in a reusable snippet. Let's rework it to use a sub-template. We should start by adding another template to our XML file, inside the
<templates> element, after the <t t-name="kanban-box"> node, as shown in the following:<t t-name="follower_avatars"> <div>
<t t-foreach="record.message_parter_ids.raw_value.slice(0, 3)"
t-as="rec">
<img t-att-src="kanban_image(
'res.partner', 'image_small', rec)"
class="oe_avatar" width="24" height="24" />
</t>
</div>
</t>
Calling it from the
kanban-box main template is quite straightforward. Instead of the <div> element containing the for each directive, we should use the following:<t t-call="follower_avatars" />
To call templates defined in other add-on modules, we need to use the
module.name full identifier, as we do with the other views. For instance, this snippet can be referred using the library_checkout.follower_avatars full identifier.
The called template runs in the same context as the caller, so any variable names available in the caller are also available when processing the called template.
A more elegant alternative is to pass arguments to the called template. This is done by setting variables inside the
t-call tag. These will be evaluated and made available in the sub-template context only and won't exist in the caller context.
We could use this to have the maximum number of follower avatars set by the caller instead of being hardcoded in the sub-template. First, we need to replace the fixed value,
3, with a variable, arg_max, example:<t t-name="follower_avatars">
<div>
<t t-foreach="record.message_parter_ids.raw_value.slice(0, arg_max)"
t-as="rec">
<img t-att-src="kanban_image('res.partner', 'image_small', rec)"
class="oe_avatar" width="24" height="24" />
</t>
</div>
</t>
Then, define that variable's value when performing the sub-template call as follows:
<t t-call="follower_avatars">
<t t-set="arg_max" t-value="3" />
</t>
The entire content inside the
t-call element is also available to the sub-template through the 0 magic variable. Instead of argument variables, we can define an HTML code fragment that can be used in the sub-template with <t t-raw="0" />. This is especially useful for building layouts and combining/nesting QWeb templates in a modular way.
We've gone through the most important QWeb directives, but there are a few more we should be aware of. Let's look at a short explanation of them.
We have seen the
t-att-NAME and t-attf-NAME style dynamic tag attributes. Additionally, the fixed t-attdirective can be used. It accepts either a key-value dictionary mapping or a pair (a two-element list).
Use the following mapping:
<p t-att="{'class': 'oe_bold', 'name': 'Hello'}" />
This results in the following:
<p class="oe_bold" name="Hello" />
Use the following pair:
<p t-att="['class', 'oe_bold']" />
This results in the following:
<p class="oe_bold" />
Thanks for sharing this Informative content. Well explained.
ReplyDeleteVisit us: Dot Net Online Training Hyderabad
Visit us: .net online training india