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 want to use a QWeb directive but 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 a t-if or a 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. On the other hand, 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:

This is not meant to be technically exact. It is just a mind map that can be useful to understand how things work in kanban views.

Next, we will learn about QWeb expressions evaluation and explore the available QWeb directives, using examples that enhance our to-do task 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 are evaluated in a context that has a few useful variables available.

A record object is available, representing the record being rendered, with the fields requested from the server. The field values can be accessed using either the raw_value or the value attributes:

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:

It is 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 >= is a valid alternative, but the common practice is to use the following alternative symbols that are available for inequality operations:

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 result. 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 a more elaborate example, we can use this directive to dynamically change the color of the Deadline Date, so that overdue dates are shown in red.

For this, replace <field name="date_deadline"/> in our kanban card with this:

<li t-attf-class="oe_kanban_text_{{ 
  record.date_deadline.raw_value and 
  !(record.date_deadline.raw_value > (new Date())) 
  ? 'red' : 'black' }}"> 
  <field name="date_deadline"/> 
</li> 

This results in either class="oe_kanban_text_red" or class="oe_kanban_text_black", depending on the deadline date. 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 better explain the point.

A block of HTML can be repeated by iterating through a loop. We can use it to add the avatars of the task followers to the task's kanban card.

Let's start by rendering just the Partner IDs of the task, 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 is 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 task 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's value, a Partner ID in this case. With this, we can rewrite the follower's 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 as prefix the variable name defined in t-as. In our example, we used rec, so the helper variables available are as follows:

For example, we could make use of the following to avoid a trailing comma on our ID list:

<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> 

QWeb templates can be reusable HTML snippet, that can be inserted in 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-name other than kanban-box. These other templates can then be included using the t-call directive. This is true for the templates declared alongside in the same kanban view, somewhere else in the same addon module, or in a different addon.

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 addon 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 full identifier todo_kanban.follower_avatars.

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's context.

We could use this to have the maximum number of follower avatars set by the caller instead of being hard-coded in the sub-template. First, we need to replace the fixed value, 3 with a variable, arg_max for 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 magic variable 0. Instead of argument variables, we can define an HTML code fragment that can be used in the sub-template with <t t-raw="0" />.