In Odoo 19, OWL stands for Odoo Web Library and acts as the frontend library for Odoo. All fields in a form view, cards in kanban view, and components on any other dashboard are rendered using OWL components.
An understanding of OWL rendering makes it easier for us to develop custom widgets. Let’s take a look at an example where we will learn about OWL rendering through the development of a Star Rating Widget.
Demo Model
We begin by creating a basic model called owl.task to experiment with the custom widgets.
# -*- coding: utf-8 -*-
from odoo import models, fields
class OwlTask(models.Model):
_name = 'owl.task'
_description = 'OWL Widget Rendering Demo Task'
name = fields.Char(required=True)
state = fields.Selection([
('new', 'New'),
('done', 'Done')
])
priority = fields.Integer(string='Priority')
The priority field will implement our custom OWL widget.
Creating the OWL Widget
OWL Widget is a JavaScript object which extends the Component class.
/** @odoo-module **/
import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { standardFieldProps } from "@web/views/fields/standard_field_props";
export class StarRatingWidget extends Component {
static template = "owl_widget_rendering_demo.StarRatingWidget";
static props = { ...standardFieldProps };
setup() {
this.state = useState({
hoverIndex: -1
});
this.stars = [1, 2, 3, 4, 5];
}
isFilled(index) {
const active = this.state.hoverIndex >= 0
? this.state.hoverIndex
: (this.props.value || 0);
return index <= active;
}
onStarClick(index) {
if (!this.props.readonly) {
this.props.record.update({
[this.props.name]: index
});
}
}
onStarHover(index) {
this.state.hoverIndex = index;
}
onStarLeave() {
this.state.hoverIndex = -1;
}
}
registry.category("fields").add("star_rating", {
component: StarRatingWidget,
displayName: "Star Rating",
supportedTypes: ["integer"],
});
Understanding the Rendering Logic
This demo shows a few key aspects of OWL rendering.
1. Reactive Rendering Using useState()
this.state = useState({
hoverIndex: -1
});When a variable is assigned to useState(), it becomes reactive. As soon as the value of hoverIndex is changed, OWL will re-render the component.
Example:
this.state.hoverIndex = index;
2. Rendering Multiple Items Using t-foreach
Within the template, stars are generated dynamically using t-foreach.
<t t-foreach="stars" t-as="star" t-key="star">
This loops through:
[1, 2, 3, 4, 5]
and creates five star elements.
Using the t-key property helps identify each star, enabling OWL to easily recycle DOM nodes when re-rendering occurs.
3. Dynamic Class Rendering
<span t-att-class="'owr-star ' + (isFilled(star) ? 'filled' : '')"
The t-att-class directive dynamically adds the CSS class.
If isFilled(star) is true, then the filled class is added, which highlights the star.
This is among the fundamental elements of OWL rendering where the user interface is automatically modified based on component state.
4. Event Handling in OWL
OWL templates support DOM event binding using t-on-*.
t-on-click="() => onStarClick(star)"
When the user clicks a star:
- onStarClick() executes
- The field value updates
- OWL re-renders the widget
Similarly:
t-on-mouseenter="() => onStarHover(star)">
updates the hover state dynamically.
OWL Template
<t t-name="owl_widget_rendering_demo.StarRatingWidget" owl="1">
<div class="owr-stars" t-on-mouseleave="onStarLeave">
<t t-foreach="stars" t-as="star" t-key="star">
<span t-att-class="'owr-star ' + (isFilled(star) ? 'filled' : '')"
t-on-click="() => this.onStarClick(star)"
t-on-mouseenter="() => this.onStarHover(star)">
?
</span>
</t>
</div>
</t>
Registering the Widget
Finally, register the widget inside the fields registry.
registry.category("fields").add("star_rating", {
component: StarRatingWidget,
displayName: "Star Rating",
supportedTypes: ["integer"],
});Now it can be used in XML views:
<field name="priority" widget="star_rating"/>
Rendering in Odoo 19 with OWL involves reactive state management and Virtual DOM rendering. By using directives such as t-foreach, t-att-class, and t-on-*, you can build very interactive widgets by writing very little code.
The given example demonstrates that OWL will render the user interface whenever there is any change in the state, which makes the widget very responsive and efficient.
To read more about Complete Overview of Widgets in Odoo 19, refer to our blog Complete Overview of Widgets in Odoo 19.