Enable Dark Mode!
overview-of-owl-component-structure-in-odoo-19.jpg
By: Yadu Krishna N P

Overview of OWL Component Structure In Odoo 19

Technical Odoo 19 Owl

Frameworks for modern web development must be quick, responsive, and simple to understand. In response to this, Odoo created its own JavaScript framework, called OWL (Odoo Web Library).

Understanding the structure and lifecycle of OWL components is essential whether you're developing a unique Odoo module, expanding pre-existing views, or developing advanced client-side interfaces. To help you create better, more dependable Odoo frontends, we'll go into great detail about the structure of an OWL component and go over each lifecycle hook with clear examples in this blog.

What is an OWL Component?

An OWL component is a JavaScript class that extends Component. It pairs a template (QWeb) with logic and manages its own reactive state. A parent component can render a child component, making it easier to build advanced UIs from small pieces.

Here's what a basic Component Structure looks like:

import { Component, xml } from "@odoo/owl";
export class HelloWorld extends Component { 
    static template = xml` 
       <div>Hello, OWL!</div>
    `; 
}

Complete Component Structure

import { Component, useState, useRef, onMounted, onWillUnmount, xml } from "@odoo/owl";
export class UserCard extends Component {
    static template = xml`
        <div class="user-card">
            <h2 t-esc="props.username"/>
            <p>Messages: <t t-esc="state.messageCount"/></p>
            <button t-on-click="loadMessages">Refresh</button>
        </div>
    `;
    static props = {
        username: { type: String },
        userId: { type: Number },
    };
    static components = {}; /* child components */
    setup() {
        this.state = useState({ messageCount: 0, loading: false });
        this.containerRef = useRef("container");
        onMounted(() => {
            console.log("UserCard mounted!");
            this.loadMessages();
        });
        onWillUnmount(() => {
            console.log("UserCard is being destroyed.");
        });
    }
    /* Methods or Functions */
    async loadMessages() {
        this.state.loading = true;
        this.state.messageCount = await     
        fetchMessageCount(this.props.userId);
          this.state.loading = false;
        }
}

1.Template

The template defines what gets rendered in the DOM. OWL templates use QWeb Odoo's XML templating engine with directives like t-if, t-foreach, and t-esc.

You can define templates in two ways:

Inline (using XML tag helper):

static template = xml`
          <div t-if="state.visible">
 <t t-foreach="props.items" t-as="item" t-key="item.id">
 <span t-esc="item.name"/> 
</t>
 </div>
 `;

External XML file:

<!-- my_module/static/src/components/my_component.xml -->
<templates>
    <t t-name="my_module.MyComponent">
        <div class="my-component">
            <h1 t-esc="props.title"/>
        </div>
    </t>
</templates>
<!-- my_module/static/src/components/my_component.js -->
static template = "my_module.MyComponent";

2. Props

The inputs a parent provides to a child component are known as props.

static props = {
    title: { type: String },
    count: { type: Number, optional: true },
    onSave: { type: Function },
    tags: { type: Array },
};
static defaultProps = {
    count: 0,
};

The child component contains read only props. Components can call callback   functions passed as props or emit custom events to communicate back to the parent.

3. setup()

In OWL, everything starts in setup(). It follows a hooks-based pattern similar to React, meaning all state initialization, service injections, and lifecycle hooks must be declared within the setup() function.

setup() {
    // State
    this.state = useState({ count: 0 });
    // Services
    this.orm = useService("orm");
    this.notification = useService("notification");
    // DOM ref
    this.inputRef = useRef("myInput");
    // Lifecycle hooks
    onMounted(() => { /* ... */ });
    onWillUnmount(() => { /* ... */ });
}

LifeCycle Hooks

OWL offers a comprehensive set of lifecycle hooks that allow you to interact with every phase of a component’s life, from its creation all the way to its destruction. Here’s a full walkthrough.

onwillStart - Before Initial Render

This hook executes once, asynchronously, before the component is rendered for the first time. It’s especially useful for tasks like loading data from a server or fetching external assets. By using onWillStart, you can ensure that all required data and resources are ready before the component appears on screen, resulting in a smoother and more seamless user experience.

import { onWillStart } from "@odoo/owl";
setup() {
   onWillStart(async () => {
     this.data = await fetchData();
   });
}

The component won't render until this async function completes. Use it for critical data fetching.

onMounted - After First Render

The mounted hook runs after a component’s DOM has been inserted into the page. It’s the right place for DOM manipulation, initializing third-party libraries, starting subscriptions, adding event listeners, or taking measurements. This hook is triggered after the first render and every time the component is attached to the DOM, ensuring the component is fully integrated and ready for user interaction.

import { onMounted } from "@odoo/owl";
setup() { 
     onMounted(() => { 
        window.addEventListener("scroll", this.handleScroll); 
     }); 
}

onWillRender - Just Before Every Render

The onWillRender hook runs synchronously just before the component’s template function executes, both on the initial render and on every subsequent re-render. It is called in a parent first, then children order. This hook allows developers to run code right before rendering, making it useful for adjusting component data, preparing variables, or performing last-minute calculations needed for the template.

import { onWillRender } from "@odoo/owl";
setup() {
  onWillRender(() => {
     console.log("Component about to render");
  });
}

onRendered - Just After Every Render

The rendered hook runs synchronously right after the component’s template function executes, both on the initial render and on every re-render. At this stage, the actual DOM may not yet exist (during the first render) or may not have been updated, as DOM updates occur in the next animation frame once all components are ready. This hook allows developers to run code immediately after rendering logic completes, making it useful for handling post-render operations or preparing interactions that depend on the component’s rendered state.

import { onRendered } from "@odoo/owl";
setup() {
  onRendered(() => {
      console.log("Component rendered");
  });
}

onWillUpdateProps - Before Props Change

The onWillUpdateProps hook runs before a component receives new props from its parent. It is asynchronous and provides the ideal place to respond to incoming prop changes, such as resetting state, fetching new data, or making API calls based on an updated ID. By using onWillUpdateProps, developers can handle any logic that depends on the new props before the component re-renders, ensuring everything is properly prepared in advance.

import { onWillUpdateProps } from "@odoo/owl";
setup() { 
     onWillUpdateProps(async (nextProps) => {
        this.data = await fetchData(nextProps.id); 
     });
 }

onWillPatch - Before DOM Update

The onWillPatch hook runs synchronously just before the DOM is patched due to a state or props change. It allows developers to read and capture information from the current DOM state, such as scroll positions, element sizes, or other measurements, before the update takes place. This makes it especially useful for preserving or reacting to DOM related details during re-renders.

import { onWillPatch } from "@odoo/owl";
setup() {
    onWillPatch(() => {
      this.scrollState = this.getScrollSTate();
    });
}

onPatched - After DOM Update

The patched hook runs synchronously after the DOM has been updated following a state or props change. It pairs well with onWillPatch, allowing developers to restore scroll positions, reapply DOM state, or update third-party libraries that depend on DOM changes. Triggered after every successful DOM update, this hook provides an opportunity to interact with the updated elements and ensure the component’s visual output stays fully in sync with its internal state and the surrounding DOM.

import { onPatched } from "@odoo/owl";
setup() {
  onPatched(() => {
    const element = document.getElementById("myElement");
    element.classList.add("highlight");
  });
}

onWillUnmount - Before Destruction

The onWillUnmount hook runs once, just before a component is removed from the DOM. It’s primarily used for cleanup tasks such as clearing timers, removing event listeners, canceling pending requests, closing WebSocket connections, or destroying third-party library instances. By using willUnmount, developers can ensure the component is properly cleaned up, and no unnecessary resources remain after it is detached from the DOM.

import { onMounted, onWillUnmount } from "@odoo/owl";
setup() {
  onMounted(() => {
    window.addEventListener("resize", this.handleResize);
  });
  onWillUnmount(() => {
    window.removeEventListener("resize", this.handleResize);
  });
}

onWillDestroy - Final Cleanup

The onWillDestroy hook is similar to willUnmount, but it runs slightly later in the teardown process after the component has been removed from the virtual DOM tree. It is always called when a component is destroyed, whether or not it was ever mounted. This makes it a reliable place to perform final cleanup tasks such as releasing resources, clearing timers, or handling any remaining teardown logic. In most cases, however, onWillUnmount is sufficient for typical cleanup needs.

import { onWillDestroy } from "@odoo/owl";
setup() {
  onWillDestroy(() => {
// Clean up resources
   this.cleanup();
  });
}

onError - Error Boundary

The onError hook catches errors thrown by child components, turning a component into an error boundary. It allows developers to gracefully handle failures within its subtree by implementing custom error-handling logic such as displaying a fallback UI, showing error messages, logging issues for debugging, or attempting recovery. By using onError, applications can remain stable and provide a smoother user experience even when unexpected errors occur.

import { onError } from "@odoo/owl";
setup() {
  onError((error) => {
    console.error("Component error:", error);
// Perform error handling logic
  });
}

OWL represents a significant leap forward for Odoo's frontend architecture. By embracing a component driven, hooks based model, it makes building and maintaining complex UIs far more intuitive than the old Widget system ever could.

Understanding the component structure templates, props, setup(), and child components gives you a solid foundation. Mastering the lifecycle hooks gives you precise control over every stage of your component's existence.

To read more about An Overview of OWL Components in Odoo 18, refer to our blog An Overview of OWL Components in Odoo 18.


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



0
Comments



Leave a comment



whatsapp_icon
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, KINFRA Techno Park
Kakkanchery, 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