Master New OWL 2 Features
Introduction
First, Odoo 18 OWL JS delivers a powerful reactive framework for building dynamic UIs. Furthermore, it streamlines your JavaScript development by cutting boilerplate code significantly. Next, this tutorial will guide you step-by-step through each new feature so you can upgrade your Odoo widgets confidently. Finally, you will see clear code examples and explanations that help you adopt OWL 2 quickly.
source : https://www.pysquad.com/blogs/odoo-18-owl-js-key-improvements-over-odoo-17?utm_source=pocket_saves
Why Upgrade to Odoo 18 OWL JS
First, Odoo 18 OWL JS enhances performance with automatic updates that reflect state changes instantly. Next, it provides new lifecycle hooks that let you run code at precise moments in a component’s life. Moreover, built-in props validation catches errors early and reduces runtime bugs. Consequently, you will write cleaner code with fewer headaches. Finally, dynamic components and slots give you flexible UI patterns that scale as your application grows.
Prerequisites
First, ensure that you run Odoo 18 on your development machine. Next, install Node.js v16+ and Yarn (or npm), because OWL 2 uses modern ES6+ features. Moreover, you will need a working Python environment for Odoo server development. Consequently, you should clone an existing custom module or create a fresh one by running:
# create a new module scaffold
$ odoo scaffold owl_tutorial addons/
Finally, you will add OWL components under your module’s static/src
folder.
Setting Up Your Odoo 18 OWL JS Environment
First, navigate to your module directory and install JavaScript dependencies:
$ cd addons/owl_tutorial/static/src
$ yarn init -y # or npm init -y
$ yarn add owl @odoo/owl axios # or npm install owl @odoo/owl axios
Next, configure your build pipeline by creating a webpack.config.js
file:
// webpack.config.js
const odoo = require('@odoo/webpack-module');
module.exports = odoo({
input: './src/index.js',
output: { filename: 'bundle.js' },
});
Furthermore, link the bundle in your module’s manifest (__manifest__.py
):
'assets': {
'web.assets_backend': [
'owl_tutorial/static/src/bundle.js',
],
},
Finally, restart your Odoo server and refresh your app to load the new OWL 2 bundle.
Key OWL 2 Improvements in Odoo 18 OWL JS
Below, you will find detailed examples and explanations for each major enhancement.
1. Reactive State Management
Code Example
// counter.js
import { Component, reactive, tags, mount } from "@odoo/owl";
class Counter extends Component {
setup() {
this.state = reactive({ count: 0 });
}
increment() {
this.state.count++;
}
}
Counter.template = tags.xml/* xml */`
<div>
<button t-on-click="increment">Increment</button>
<span>Count: <t t-esc="state.count"/></span>
</div>`;
mount(Counter, { target: document.body });
Explanation
First, you define a reactive object with reactive({ count: 0 })
. Next, when you call this.state.count++
, OWL 2 updates the DOM automatically. Moreover, you avoid manual this.render()
calls and keep your code concise. Consequently, state changes flow ahead without extra work.
2. New Lifecycle Hooks
Code Example
// logger.js
import { Component, onMounted, onWillStart, onWillUnmount, tags, mount } from "@odoo/owl";
class Logger extends Component {
setup() {
onWillStart(() => console.log("Will start"));
onMounted(() => console.log("Mounted"));
onWillUnmount(() => console.log("Will unmount"));
}
}
Logger.template = tags.xml`<div>Check console logs</div>`;
const app = mount(Logger, { target: document.body });
setTimeout(() => app.destroy(), 5000);
Explanation
First, onWillStart
runs before initial rendering. Next, onMounted
fires after the component appears in the DOM. Moreover, onWillUnmount
triggers before cleanup. Consequently, you gain precise control over setup and teardown logic.
3. Reactive Arrays and Objects
Code Example
// todo.js
import { Component, reactive, tags, mount } from "@odoo/owl";
class Todo extends Component {
setup() {
this.state = reactive({ items: ["Task 1"] });
}
addItem() {
this.state.items.push(`Task ${this.state.items.length + 1}`);
}
}
Todo.template = tags.xml`
<div>
<button t-on-click="addItem">Add Task</button>
<ul>
<t t-foreach="state.items" t-as="item">
<li><t t-esc="item"/></li>
</t>
</ul>
</div>`;
mount(Todo, { target: document.body });
Explanation
First, wrapping items
in reactive
lets OWL detect array mutations. Next, calling .push()
re-renders the <ul>
list automatically. Moreover, you avoid replacing entire arrays and keep your code efficient.
4. Improved Template Handling
Code Example
<!-- template.xml -->
<t t-name="MyComponent">
<div>
<t t-if="props.showMessage">
<p><t t-esc="props.message"/></p>
</t>
<t t-else="">
<p>No message available</p>
</t>
</div>
</t>
Explanation
First, OWL 2 compiles templates faster with an improved parser. Next, the t-if
and t-else
directives execute reliably. Moreover, you write cleaner XML without extra wrappers.
5. Props Validation
Code Example
// greeting.js
import { Component, tags, mount } from "@odoo/owl";
class Greeting extends Component {}
Greeting.props = {
name: { type: String, required: true },
age: { type: Number, default: 0 },
};
Greeting.template = tags.xml`
<div>Hello, <t t-esc="props.name"/>! You are <t t-esc="props.age"/>.</div>`;
mount(Greeting, { target: document.body, props: { name: "Sam", age: 28 } });
Explanation
First, OWL throws an error if you omit name
or pass a wrong type. Next, default values apply when you skip optional props. Moreover, prop validation reduces runtime bugs significantly.
6. Enhanced Component Slots
Code Example
// card.js
import { Component, tags, mount } from "@odoo/owl";
class Card extends Component {}
Card.template = tags.xml`
<div class="card">
<header><slot name="header"/></header>
<main><slot/></main>
<footer><slot name="footer"/></footer>
</div>`;
class Demo extends Component {}
Demo.components = { Card };
Demo.template = tags.xml`
<Card>
<t t-slot="header">Report</t>
<p>Main content here</p>
<t t-slot="footer">© 2025</t>
</Card>`;
mount(Demo, { target: document.body });
Explanation
First, you define multiple slots by name. Next, you pass header and footer content dynamically. Moreover, default slot handles main content. Consequently, you build flexible layouts with minimal code.
7. Cleaner Event Binding
Code Example
<t t-name="Clicker">
<button t-on-click="() => alert('Clicked!')">Click Me</button>
</t>
Explanation
First, OWL replaces verbose syntax with t-on-click
. Next, you attach handlers inline or via methods. Moreover, you avoid confusion with v-on:
or other frameworks’ syntax.
8. Dynamic Component Support
Code Example
<t t-name="DynamicDemo">
<t t-set="cmp" t-value="state.currentComponent"/>
<t t-component="cmp"/>
</t>
// dynamic.js
import { Component, reactive, mount } from "@odoo/owl";
class Greeting extends Component { /* … */ }
class Farewell extends Component { /* … */ }
const components = { Greeting, Farewell };
class DynamicDemo extends Component {
setup() {
this.state = reactive({ currentComponent: "Greeting" });
}
toggle() {
this.state.currentComponent = this.state.currentComponent === "Greeting" ? "Farewell" : "Greeting";
}
}
DynamicDemo.components = components;
DynamicDemo.template = tags.xml`
<div>
<button t-on-click="toggle">Toggle</button>
<t t-component="state.currentComponent"/>
</div>`;
mount(DynamicDemo, { target: document.body });
Explanation
First, you store component names in state. Next, <t-component>
renders the matching class. Moreover, you switch dynamically without rebuilding the whole app.
9. Improved Modern JavaScript Support
Code Example
// datafetch.js
import { Component, tags, mount } from "@odoo/owl";
import axios from "axios";
class DataFetch extends Component {
async setup() {
const { data } = await axios.get("/api/items");
this.state = { items: data.items };
}
}
DataFetch.template = tags.xml`
<ul>
<t t-foreach="state.items" t-as="item"><li><t t-esc="item"/></li></t>
</ul>`;
mount(DataFetch, { target: document.body });
Explanation
First, you use async/await
for clarity. Next, destructuring pulls data.items
easily. Moreover, ES modules keep your code modular and maintainable.
10. New API for Component Rendering
Code Example
// main.js
import { mount } from "@odoo/owl";
import App from "./app.js";
const app = mount(App, {
target: document.getElementById("app"),
props: { user: "Admin" },
});
Explanation
First, you call mount()
once to bootstrap your app. Next, OWL handles rendering and updates internally. Moreover, you avoid manual attachment logic and focus on component design.
11. Simplified Component Unmounting
Code Example
// teardown.js
import { mount } from "@odoo/owl";
import App from "./app.js";
const app = mount(App, { target: "#app" });
// … later
app.destroy();
Explanation
First, destroy()
removes event listeners and DOM nodes cleanly. Next, you prevent memory leaks by tearing down components explicitly. Moreover, OWL handles all cleanup—so you don’t need manual code.
12. How PySquad Can Help with Odoo 18 OWL JS
First, PySquad offers migration services from Odoo 17 to Odoo 18, ensuring a smooth transition to OWL 2. Next, PySquad delivers hands-on training so your team adopts reactive patterns effectively. Moreover, PySquad refactors legacy widgets and optimizes your front-end performance. Finally, PySquad provides ongoing support to keep your Odoo installation up to date and bug-free.
Migration Guide: Upgrading from Odoo 17 OWL to Odoo 18 OWL JS
First, audit your existing OWL 1 widgets and identify manual render calls. Next, update lifecycle methods from mounted()
to onMounted()
and willUnmount()
to onWillUnmount()
. Moreover, wrap your state objects and arrays in reactive()
to leverage automatic updates. Consequently, you will remove redundant this.render()
calls. Afterwards, validate props by adding the props
static field with type definitions. Finally, test each component in isolation to confirm that UI updates behave correctly.
Best Practices for Odoo 18 OWL JS Development
First, use reactive state sparingly to avoid over-rendering. Next, group related data in a single reactive object. Moreover, add key
attributes in t-foreach
loops to help OWL track nodes. Consequently, you will improve performance. Additionally, validate props to catch bugs early. Furthermore, destroy components when you no longer need them to free memory. Finally, follow the official Odoo OWL documentation{:target=”_blank”} for API updates.
Troubleshooting Common OWL 2 Issues
State Not Updating
First, verify that you wrapped your data in reactive()
. Next, avoid direct mutations on plain objects. Moreover, ensure that you call component methods correctly with t-on-click="methodName"
.
Lifecycle Hooks Not Firing
First, check that you imported the hooks from @odoo/owl
. Next, confirm that setup()
runs before you call hooks. Moreover, ensure that you mount your component with mount()
instead of a custom renderer.
Template Compilation Errors
First, validate your XML syntax carefully. Next, run your build tool to catch compile-time errors. Moreover, refer to the official docs for directive usage.
Conclusion
First, Odoo 18 OWL JS brings a modern, reactive framework that simplifies front-end development. Next, its new lifecycle hooks, reactive state management, and props validation improve code quality and maintainability. Moreover, dynamic component support and enhanced slots give you great flexibility in building UIs. Consequently, you can deliver richer features with less code and fewer errors. Finally, by following this tutorial and best practices, you will accelerate your Odoo development workflow.
Frequently Asked Questions
What versions of Odoo support OWL 2?
First, OWL 2 arrives in Odoo 18, and earlier versions remain on OWL 1 or older JS frameworks.
Can I use OWL 2 in community versus enterprise?
First, OWL 2 works identically in both editions, because it lives on the front-end.
How do I debug OWL components?
First, use browser dev tools to inspect DOM updates. Next, add console.log
statements within lifecycle hooks. Moreover, enable source maps in your build to see original code.
Where can I learn more?
First, visit the Odoo OWL documentation{:target=”_blank”} for in-depth API details.
Discover more from teguhteja.id
Subscribe to get the latest posts sent to your email.