Enable Dark Mode!
an-overview-of-qweb-templates-and-operations-in-odoo-17.jpg
By: Cybrosys Technologies

An Overview of Qweb Templates & Operations in Odoo 17

Technical

QWeb specifications serve as a framework for defining Owl templates, primarily employed for HTML generation and built on the XML format. These templates are organized into OWL functions, general a virtual DOM representation of the HTML. Owl, being a dynamic component system, introduces its distinct directives, such as the t-on directive.(like t-on)

<div>
    <span t-if="Condition" >Sample string</span>
    <ul t-else=" >
        <li t-foreach= "iterative message" t-as= "message" >
            < t t-esc= "message" / >
        </li>
    </ul>
</div>

Now, we have to discuss some common directives.


Name 

Description

t-escOutputting safe a value
toutOutputting value, possible without escaping
t-if, t-elif, t-else,Constitutional rendering
t-set, t-valueSetting a variable
t-foreach, t-asLoops
t-att, t-attf-*, t-att-*Dynamic attributes
t-callRendering sub-template
t-debug, t-logDebugging
t-translationDisabling the translation of a node

In Owl, additional directives are requested beyond those specified above. The following directives are necessary:


Name

Description

t-component, t-propsDefining subcomponent
t-tagRendering nodes with dynamic tag name
t-keyDefining a key
t-slot, t-set-slot, t-slot-scopeRendering a slots
t-refSetting a reference to a dom node or a sub-component
t-modelForm input binding
t-on-*Event handling
t-portal Portal

Now we can see each other deeply.

Data output

Wherever you want to add a dynamic expression, here we have to use these directives (t-esc and t-out).

* t-esc

T-esc directives are used to render a value of the field or a variable into a template.

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

In the above code, the value of the variable set is 20. Then, its output is shown below.

< p>20</p>

* tout

The t-out directive closely resembles t-esc, expect it may not involve escaping. The distinction lies in the fact that the value received by the t-out directive remains unescaped only when specically marked as such through the markup utility function.

For example,

<div>
    <span t-if="Condition" >Sample string</span>
    <ul t-else=" >
        <li t-foreach= "iterative message" t-as= "message" >
            < t t-esc= "message" / >
        </li>
    </ul>
</div>

Initially, the initial t-out functions similarly to a t-esc directive, resulting in the escapement of the content in value1. Nevertheless, as value2 has been designed as markup, it will be inserted as HTML.

Conditionals

QWeb incorporates a conditional directive 'if,' which esses an expression provided as an attribute value:

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

If it's the condition is true, then it renders the value “ ok”

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

If it's false, it's removed from the result.

<div>
</div>

Extra conditional directives branches:

<div>
    < p t-if="user.birthday = = today () ">Happy birthday!</p>
    < p t-elif="user.login = = 'root'" >Welcome master!</p>
    < p t-else=" >Welcome!</p>
</div>

Variable Setting

In QWeb, the creation of a variable to store data for subsequent computations, allowing for multiple usages, is achieved through the t-set directive. This directive is employed to store data under the specified name (variable name).

There are two ways to provide the value to be set:

A 't-value' attribute can be utilized, containing an expression whoevaluation result will be set:

<div>
    < p t-if="user.birthday = = today () ">Happy birthday!</p>
    < p t-elif="user.login = = 'root'" >Welcome master!</p>
    < p t-else=" >Welcome!</p>
</div>

It's essential to note that the evaluation occurs at rendering time, not during compile time.

Loops

In QWeb templates, the 't-foreach' directive is employed for iterating through items. The 't-as' directive is utilized to assign a name to the current item with the iteration loop.

< t t-foreach=" [1,3,5,7] "t-as=" i ">
    < p> < t-out = "i" / > < / p>
</t>

Its render as:

< p>1</p>
< p>3</p>
< p>5</p>
< p> 7</p>

Similar to conditions, 't-foreach' applies to the element that carries the directive's attribute and

< p t-foreach=" [1, 3, 5,7] "t-as="i "t-key="i ">
    < t t-esc="i"/>
</p>
The statement "is equalent to the previous example" is a repetition of the context, so it can be omitted for conciseness:

An important distortion should be noted in comparison to the usual QWeb behavior: Owl mandates the presence of a 't-key' directive for prop reconciliation of renderings.

The 't-foreach' directive can iterate over any iterable and offers special support for objects and maps. It revels the 't-as' content as the key of the ongoing iteration and associates the corresponding value using the same name along with the '_value' suffix.

Beyond the specified name conveyed through 't-as,' 't-foreach' furnishes several other useful variables (please note that $as will be substituted with the provided 't-as' name):

* $as_value: it denotes the current iteration's value, identical to $as for arrays and similar iterables; for objects and maps, it supplies the value, while $as provides the key.

* $as_index: It specifics the current iteration index (the initial item in the iteration holders an index of 0).

* $as_first: It indicates where the present item is the first in the iteration (equivalent to $as_index = = 0).

* $as_last: signatures when the ongoing item is the last in the iteration (equivalent to $as_index + 1 = = $ as_size), contingent upon the iteratee's sizing.

These supplementary variables, along with any newly generated ones within 't-foreach,' remain exclusively accessible within the 't-foreach' scope. Should a variable persist beyond the confines of 't-foreach,' its value is duplicated into the global context upon the 'foreach's conclusion.

< t t-set= "existing_variable" t-value= "false" / >
<!-- existing_variable now False -->
< p t-foreach="Array (3) "t-as=" i "t-key=" 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 -->

While Owl strives for declarativity, the DOM doesn't fully expose its state declaratively in the DOM tree. Critical elements like scrolling state, current user selection, focused elements, or input state aren't set as attributes in the DOM tree. This is why a virtual DOM algorithm is employed to retain the actual DOM node instead of replacing it with a new one.

Consider a scenario where we have a list of two items [{ text: "a" }, { text: "b" }] and render them using the template:

< p t-foreach= "items" t-as="item" t-key= "item_index" > < t-esc= "item.text"/> < / p>

The result yields two `< p>` tags with the text 'a' and 'b'. Now, if we swap them and re-render the template, Owl needs to determine the intent:

- Should Owl swap the DOM nodes?

- Or should it keep the DOM nodes but update the text content?

This seemingly trivial decision matters, leading to different outs in scenarios like maintaining user text selection during swaps.

The `t-key` directive is crucial for assigning identity or uniqueness to an element, enabling Owl to discern where the elements in a list are unique.

For instance, adding an ID to the above example ([{id: 1, text: "a" }, {id: 2, text: "b" }]) shows the template to be modified:

< p t-foreach= "items" t-as="item" t-key= "item.id"> < t-esc= "item.text"/> < / p>

The `t-key` directive is valid for lists (`t-foreach`). A key should be a unique number or string (objects won't work, as they are cast to the "[object Object]" string, which is not unique).

Additionally, the key can be set on a `t` tag or its child, with the following variations being equalent:

< p t-foreach= "items" t-as="item" t-key= "item.id" >
  < t t-esc="item.text"/>
</p>
< t t-foreach= "items" t-as="item" t-key= "item.id" >
  < p t-esc="item.text"/>
</t>
< t t-foreach= "items" t-as="item ">
  < p t-key= "item.id" t-esc= "item.text"/>
</t>

In the absence of a `t-key` directive, Owl defaults to using the index.

Note: The `t-foreach` directive exclusively accesss arrays or objects, not other iterables like Set. However, using the JavaScript spread operator (...) can convert sets (or other iterables) into lists for compatibility:

< t t-foreach=" [...items]" t-as= "item" >...</t>

Dynamic sub-templates

QWeb templates serve not only for top-level rendering but also for inclusion within other templates, eliminating redundancy and enabling code reduction and optimization through reuse. This is facilitated by the use of the `t-call` directive.

For example:

<div t-name= "other-template" >
    < p> < t-value="var"/> < / p>
</div>
<div t-name= "main-template" >
    < t t-set="var" t-value= "owl"/>
    < t t-call="other-template "/ >
</div>

This structure renders as `< div> < p> owl< / p> < / div> `. The sub-template is essentially inlined with the main template but operates in a sub-scope where variables defined in the sub-template do not escape.

In situations where information needs to be passed to the sub-template, the content of the `t-call` directive 's body is available as a special magic variable `0`:

< t t-name="other-template ">
    This template was called with content:
    < t-raw="0"/>
</t>
<div t-name= "main-template" >
    < t t-call= "other-template" >
        <em>content</em>
    </t>
</div>

This results in:

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

This mechanism allows for defining variables scoped to a sub-template:

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

Note: By default, the rendering context for a sub-template is the current rendering context. However, specifying a specific object as context is possible using the `t-call-context` directive:

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

Dynamic sub-templates

The `t-call` directive is versatile, allowing dynamic sub-template invocation through string interpolation. For instance:

<div t-name= "main-template" >
    < t t-call=" { { template} } ">
        <em>content</em>
    </t>
</div>

In this scenario, the template's name is acquired from the `template` value within the template rendering context, facilitating dynamic and flexible template calls.

Inline templates

In many real-world applications, templates are typically defined in an XML file to leverage the XML ecosystem and perform additional processing, such as translation. However, there are cases where defining a template inline can be convenient. To achieve this, the `xml` helper function can be employed:

const { Component, xml } = owl;
class MyComponent extends Component {
  static template = xml`
      <div>
          <span t-if="somecondition" > text</span>
          <button t-on-click= "someMethod" >Click< /button>
      </div>
  `;
  // ... other component logic
}
mount (MyComponent, document.body);

This function generates a unique string ID, registers the template under that ID within Owl's internals, and then returns the ID. This approach enables the online definition of templates for certain scenarios.

Rendering svg

class Node extends Component {
  static template = xml`
        < g>
            <circle t-att-cx= "props.x" t-att-cy= "props.y" r= "4" fill="black"/>
            < text t-att-x= "props.x - 5" t-att-y="props.y + 18 "> < t-esc=" props.node.label "/ > </text
            < t t-set= "childx" t-value= "props.x + 100" / >
            < t t-set= "height" t-value= "props.height/(props.node.children | | []).length"/>
            < t t-foreach="props.node.children | | [] "t-as=" child ">
                < t t-set= "childy" t-value= "props.y + child_index*height"/>
                <line t-att-x1="props.x" t-att-y1="props.y" t-att-x2="childx" t-att-y2="childy "stroke= "black" / >
                <Node x="childx" y= "childy" node= "child" height= "height"/>
            </t>
        </g>
    `;
  static components = { Node };
}
class RootNode extends Component {
  static template = xml`
        < svg height="180" >
            <Node node="graph" x="10 "y="20 "height="180 "/ >
        </svg>
    `;
  static components = { Node };
  graph = {
    label: "a",
    children: [
      { label: "b" },
      { label: "c", children: [{ label: "d" }, { label: "e" }] },
      { label: "f", children: [{ label: "g" }] },
    ],
  };
}

The `RootNode` component is responsible for rendering a dynamic SVG representation of the graph specified by the `graph` property. It's important to note that there is a recurring structure in play, as the `node` component utilizes itself as a subcomponent.

A crucial conscience is that Owl requires a prop namespace setting for each SVG element. Due to Owl compiling each template independently, it faces challenges in easily determining where a template is intended for inclusion in an SVG namespace. To address this, Owl relies on a heuristic: if a tag is either `svg`, `g`, or `path`, it is treated as an SVG element. In practical terms, this implies that each component or sub-template (included with `t-call`) should have one of these tags as its root element. This heuristic helps Owl appropriately handle SVG namespaces in the rendered output.

In summer, Odoo 17's QWeb templates and operations provide a dynamic and user-friendly approach to customizing and enhancing the platform's functionality. These tools empower businesses to craft visually appealing reports, documents, and views. The flexibility offered by operations such as loops and conditions further enables the creation of tailored solutions to meet specific business needs.

To read more about what is the QWeb template in Odoo 17, refer to our blog What is the QWeb template in Odoo 17.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment

 


whatsapp
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message