Odoo QWEB Templates - Python & Javascript

In this tutorial, we are going to continue QWEB Tutorials Open . In the previous tutorial, we already did some of the basics to moderate functionalities. Now we will do more advanced functionalities like caching Open , inheritance, and QWEB using JavaScript Open .

For those who didn't watch my previous tutorial, you can check the video below.

Odoo QWEB Templates - Python & Javascript

Cache/Nocache

Let's start with caching and here we are going to use t-cache and t-nocache directives. The t-cache method is used to make pages load faster since any QWEB t directives will be rendered only once and thus, lessen any SQL queries. This is nice for the website to load faster. This doesn't mean the template will not fetch updated content since it will automatically re-render if the cache key has been changed.

Say for example in a model sale order, if we loop to each record using t-foreach and we set an element to be cached, by passing each model record, that element then will only re-render if the current record has been changed. By default, if you pass a recordset, it will automatically use the model ID and write date as the cache key.

Cache using boolean value

We can also use a boolean value in t-cache wherein if it’s a false value, the cache will be automatically invalidated. For example in the sales order count, we don’t want to cache it for logged-in users but will be cached for public users.

Use nocache inside cache

You might want to just specify which element should not be cached inside an element with t-cache. Say for example below, inside an element with t-chache, the t-nocache directive has been added to the sale order count. You can add any value as a reference if you want or leave it blank.

It is important to note that t-nocache will only accept values coming from the controller render method. So adding a template variable having values coming either from the controller or directly from the template, it will not be rendered inside an element with t-cache. In addition, t-cache is scoped which means setting values inside it will not work outside that element.

What you can do, however, if you want to render any template variables, you need to add primitive values after the t-nocache directive. Just make sure you add it with an element with an existing t-nocache directive. In this way, you can render it using a normal t-out directive.

Inheritance using Python

Since Python and Javascript have different ways of creating a template, let’s first do the Python Inheritance. There are two modes in template inheritance, either as an extension, where in every change you make, will affect the main template. Or as a child template which will not affect the main template but will create a new version out of it.

Extension Inheritance

For extension inheritance, create a new template with id main_template_inherited and inherit_id which will be the template you want to inherit. Using XPATH, you can target any elements present in the main template using the expr attribute. If you want to start from the root element, just add the root element which is div followed by slash, then the next element you want to select.

After selecting the target element, there are options where you want to render the element by adding a position attribute which can either be inside, before, after, or attributes.

Primary Inheritance

Now if you want to create a child template so that it doesn’t affect the main template, you need to add a primary attribute that is equal to True. For the example below, using XPATH, it targets the div element and use the position attributes. You can add a class, remove a class, and add a separator. You can also set a new attribute and add some styles.

Javascript - Public Widget

Rendering Template

We will be doing several options on how you can use it for the website pages. In order to create a public widget, import public Widget from web/legacy/js/public/public_widget and register a new widget by extending publicWidget.Widget. Ths method, needs an object parameter. The first property needed is the selector. The value will be any CSS selector from the website page. This is important since this will be the basis of the widget in order to be activated.

Data Output, Looping, Attributes

Now let’s try to do data output, variables, and looping. Create a new template and inside that template, render a string, a javascript object, an array, model, and setting attributes. Then create a new public widget and the names will be similar to the name of the template. For this example, we are going to pass some data needed on the template so we need to use renderToElement method from web/core/utils/render.

Under the start method, create a new constant variable content that is equal to renderToElement by passing the widget template followed by an object that will hold the values needed by the template. Add a string, array and email. Under the template and render the string, for javascript object, let’s try to set it directly from the template as some_date with value as new Date(). Then render it using t-out some_date.toLocalString() format.

Then in array, use the t-foreach directive, this will be the same in the Python code except that we need to add t-key directive. Then use t-out to render each item. For the attributes, add an anchor element and use the t-attf attribute followed by the href attribute, add mailto, and inject the email data using double curly brackets, and render the email using t-out.

Going back to the widget, the good thing about the renderToElement method is that we can attach it to any selector. In this case, let’s render it at the root element using this.$target and use HTML method, then pass the content variable holding the rendered template.

Lastly, create a new div element having a class we defined under the widget selector property.

To render some models to the template, we need to use the ORM service. We then need to initialize it under the init method but don’t forget to add a super method in order to not break some code. By default, services are already available in the public widget by calling the bindService method and passing the service name.

Just to note that this method is for version 17, if you want to implement this in version 16, you need to import public widget from web.public.widget and when rendering it to an element, import qweb from web.core and use the render method. When interacting with the backend, use the built-in in _rpc method.

In this example, we will use bindService and pass orm as the parameter. Since the orm service will return a promise, add async await to the start method and then finally call the orm service by using the searchRead method.Under the template, render it using t-foreach and render the name field using t-out.

Template Inheritance

Now let’s move on to template inheritance. Create a new template and similar to the Python code templates, there are two modes in template inheritance. Either extension or primary.

JS Extension Inheritance

Duplicate the main template but instead of name, change it to inherit. This mode deosn’t need to have a t-name attribute. We can define the mode by using t-inherit-mode directive and by default, the mode will be primary if you didn’t define it.

JS Primary Inheritance

Duplicate the main template extension for the primary mode and since this is primary we need to add a t-name and. This mode will create a child template where the changes you make will not affect the parent template. Let’s then add a new button.

To render it, create a new widget and name it same with the main template. In rendering the child template, let's try to do other methods using document.querySelector and pass this new class. After that, use innerHtml property to add some content and use the renderToElement method, passing the name of the main template child then calling outer HTML property. There’s also innerHTML property, however, this will not include the root element.

Lastly, add the selectors in Python QWEB Template in rendering to the page.

Sub-Teplate

Let's do to the sub-template. Create a new template with name subTemplate and inside, create a root element with an ID wrapper.

Add a header, footer, and main. Inside main, render a sub-template using t-call directive. Then create a new widget subTemplate, rename the selector, and template, create a new div element with class js_sub_template.

Adding Events

Next, add some events to a template. Create a new template and name it as templateWithEvents. Inside this template, create a span element that will hold a counter variable and a button that will increase the counter value.

Then create a new widget that is the same as the name of the template, and for the selector, name it to js_template_with_events. So in adding an events to widget, we need to define it under the events property. The value will be a list of key-value pairs. The key will be a string where the first string is the name of the event like click followed by the selector like a button element or any class or id. The value will be a string which is the name of the call-back function.

Let’s name it onClick and inside this method, let’s increase the counter field class. We then need to add the init method and initialize this.counter is equal to 0. Then we just need to target the element we want to render this counter using the find method having an ID as a counter. Let’s modify the text by the counter.

Then add a new div with class js_template_with_events.

But let’s check some issues here. If you inspect it under the network tab and open the document, the preview will show you that all those JS templates will not be loaded immediately. But only when the assets have been downloaded successfully. So this might affect the SEO. To improve SEO, we need to transfer the template to Python QWEB templates so that it will be automatically rendered. You can also use the t-cache method to minimize SQL database queries.

Then some other client action, you can pass it using the widget. Now the idea here is to render initial content for SEO purposes and once the assets have been loaded, you can then render other parts wherein you can apply all the methods discussed above..

Watch Video Tutorial

Odoo QWEB Templates - Python & Javascript