Ana içeriğe atla

Odoo OWL Çerçevesi: Bileşenler, Destekler, Kancalar, Reaktivite

OWL için eksiksiz kılavuz — Odoo'nun web kütüphanesi: bileşenler, destekler, yaşam döngüsü kancaları, reaktif durum, referanslar, yuvalar, OWL'nin Odoo modüllerine entegrasyonu.

E
ECOSIRE Research and Development Team
|4 Mayıs 20268 dk okuma1.5k Kelime|

Bu makale şu anda yalnızca İngilizce olarak mevcuttur. Çeviri yakında eklenecektir.

OWL — Odoo Web Library — is the JavaScript framework that powers Odoo's web client since version 16. It's a homegrown reactive component framework with intentional similarities to React (components, props, hooks) and Vue (template syntax, reactivity), but with its own conventions tuned for Odoo's needs.

OWL — Odoo Web Library — is the JavaScript framework that powers Odoo's web client since version 16. It's a homegrown reactive component framework with intentional similarities to React (components, props, hooks) and Vue (template syntax, reactivity), but with its own conventions tuned for Odoo's needs. If you're writing custom views, snippet options, POS extensions, or any front-end Odoo customization, OWL is non-negotiable.

This article covers the OWL concepts you actually need to ship production code, with examples from real Odoo modules.

Key Takeaways

  • OWL components are TypeScript/JS classes extending Component with a static template
  • Props are typed via static props declarations — strict type checking at runtime
  • useState provides reactive state; mutations trigger re-renders
  • onMounted, onWillStart, onPatched, etc. are lifecycle hooks
  • useService connects to Odoo's service layer (ORM, dialogs, notifications, router)
  • Templates use XML with t-on-, t-att-, t-foreach- directives — OWL's QWeb dialect
  • Components register in web.assets_backend or scoped bundles based on use case

OWL component anatomy

A minimal OWL component:

import { Component } from "@odoo/owl";

export class HelloWorld extends Component {
    static template = "my_module.HelloWorld";
    static props = {
        name: { type: String, optional: true },
    };
}

With its template (separate XML file):

<?xml version="1.0" encoding="UTF-8"?>
<templates>
    <t t-name="my_module.HelloWorld">
        <div class="hello">
            Hello, <t t-esc="props.name or 'World'"/>!
        </div>
    </t>
</templates>

Register the component (typically via a service or as part of an action) and it's ready.

Props and prop validation

OWL enforces prop types at runtime in dev mode:

static props = {
    user: { type: Object, shape: { name: String, age: Number } },
    onSave: Function,
    items: { type: Array, element: { type: Object } },
    visible: { type: Boolean, optional: true },
    "*": true,  // allow extra props (less strict)
};

Unrecognized props or wrong types throw in dev. Production silently allows for backward compatibility, but the dev catch is invaluable.

Reactive state with useState

import { Component, useState } from "@odoo/owl";

export class Counter extends Component {
    static template = "my_module.Counter";
    
    setup() {
        this.state = useState({ count: 0 });
    }

    increment() {
        this.state.count++;  // triggers re-render
    }
}
<t t-name="my_module.Counter">
    <button t-on-click="increment">
        Count: <t t-esc="state.count"/>
    </button>
</t>

Mutations to objects/arrays in state are tracked deeply. Unlike React, you don't need spread syntax.

Lifecycle hooks

HookWhenUse case
onWillStartBefore first render, can be asyncFetch initial data
onMountedAfter component is in DOMDOM manipulation, event listeners
onWillUpdatePropsBefore re-render due to prop changeSync derived state
onPatchedAfter re-renderDOM-based side effects
onWillUnmountBefore removal from DOMCleanup listeners, timers
onErrorWhen child throwsError boundary
import { Component, useState, onWillStart, onMounted, onWillUnmount } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";

export class CustomerList extends Component {
    static template = "my_module.CustomerList";

    setup() {
        this.orm = useService("orm");
        this.state = useState({ customers: [], loading: true });
        this._timer = null;

        onWillStart(async () => {
            this.state.customers = await this.orm.searchRead(
                "res.partner",
                [["is_company", "=", true]],
                ["id", "name", "email"]
            );
            this.state.loading = false;
        });

        onMounted(() => {
            this._timer = setInterval(() => this.refresh(), 30000);
        });

        onWillUnmount(() => {
            clearInterval(this._timer);
        });
    }

    async refresh() {
        this.state.customers = await this.orm.searchRead(
            "res.partner",
            [["is_company", "=", true]],
            ["id", "name", "email"]
        );
    }
}

useService — accessing Odoo's services

useService is the canonical way to access Odoo platform features:

import { useService } from "@web/core/utils/hooks";

setup() {
    this.orm = useService("orm");           // ORM operations
    this.action = useService("action");      // open actions, navigate
    this.notification = useService("notification");  // toast notifications
    this.dialog = useService("dialog");      // open dialogs
    this.user = useService("user");          // current user info
    this.router = useService("router");      // URL state
    this.rpc = useService("rpc");            // raw RPC call
}

// Use them
await this.orm.create("res.partner", { name: "Acme" });
this.notification.add("Customer created", { type: "success" });
this.dialog.add(SomeDialogComponent, { props });

Template directives

OWL templates extend QWeb with reactive directives:

DirectivePurposeExample
t-escEscaped text<span t-esc="state.name"/>
t-outRaw HTML (careful!)<div t-out="state.html"/>
t-att-XAttribute binding<input t-att-disabled="state.locked"/>
t-attf-XFormat string attr<div t-attf-class="status-{{state.status}}"/>
t-on-XEvent handler<button t-on-click="save">Save</button>
t-if, t-elseConditional render<div t-if="state.error">Error!</div>
t-foreachLoop<li t-foreach="state.items" t-as="item" t-key="item.id">
t-refRefs to DOM<input t-ref="searchInput"/>
t-componentRender child component<t t-component="MyChild" propName="value"/>
t-slotSlot content<t t-slot="default"/>

Slots — composition

Slots let parent components pass content into child components:

<!-- Card component template -->
<t t-name="my_module.Card">
    <div class="card">
        <div class="card-header">
            <t t-slot="header"/>
        </div>
        <div class="card-body">
            <t t-slot="default"/>
        </div>
    </div>
</t>

<!-- Usage -->
<Card>
    <t t-set-slot="header">My Title</t>
    <p>Body content here</p>
</Card>

Slots are like React's children + named slots in one mechanism.

Refs to DOM

import { useRef, onMounted } from "@odoo/owl";

setup() {
    this.searchInput = useRef("searchInput");
    onMounted(() => {
        this.searchInput.el.focus();
    });
}
<input t-ref="searchInput" type="text"/>

Custom hooks

Build reusable logic via custom hooks (functions starting with use):

import { useState, onMounted, onWillUnmount } from "@odoo/owl";

export function useWindowSize() {
    const state = useState({ width: window.innerWidth, height: window.innerHeight });

    function update() {
        state.width = window.innerWidth;
        state.height = window.innerHeight;
    }

    onMounted(() => window.addEventListener("resize", update));
    onWillUnmount(() => window.removeEventListener("resize", update));

    return state;
}

// Use it in any component
setup() {
    this.size = useWindowSize();
}

Comparison to React

ConceptOWLReact
Componentclass extends Componentfunction or class extends Component
StateuseState({...}) deep reactiveuseState(value) shallow + setter
EffectonMounted, onWillUnmountuseEffect(() => {...}, [deps])
MemoizationComputed via getters in setupuseMemo, useCallback
RefsuseRef("name") + template t-refuseRef() + JSX ref=
Childrent-slot (named slots)props.children
TemplateXML with t- directivesJSX (JavaScript expressions)

Common patterns in Odoo customizations

Override an existing component

import { patch } from "@web/core/utils/patch";
import { ListController } from "@web/views/list/list_controller";

patch(ListController.prototype, {
    setup() {
        super.setup();
        // your additions
    },
    
    onCustomAction() {
        // your new method
    },
});

Register a field widget

import { Component } from "@odoo/owl";
import { registry } from "@web/core/registry";
import { standardFieldProps } from "@web/views/fields/standard_field_props";

class MyCustomField extends Component {
    static template = "my_module.MyCustomField";
    static props = { ...standardFieldProps };
}

registry.category("fields").add("my_custom", {
    component: MyCustomField,
});

Then in XML view: <field name="my_field" widget="my_custom"/>.

Asset registration

Components must register in an asset bundle:

# __manifest__.py
{
    'name': 'My Module',
    'assets': {
        'web.assets_backend': [
            'my_module/static/src/components/customer_list/*',
        ],
    },
}

For Odoo 19's split bundles, choose the right one:

  • web.assets_backend — admin/internal user features
  • web.assets_frontend — public website
  • point_of_sale.assets — POS-specific
  • website.assets_edit_frontend — website editor

Frequently Asked Questions

Is OWL similar enough to React that React developers can pick it up quickly?

Yes, with a 1-2 day ramp. The component model and hooks pattern translate directly. The biggest mental shift is the template language (XML directives instead of JSX) and the deep reactivity (mutate state directly instead of immutable updates). Production-ready OWL code from a React developer comes within a sprint.

Can I use OWL outside of Odoo?

Technically yes — OWL is open source on GitHub. But its services and asset pipeline are tightly coupled to Odoo. For non-Odoo projects, React or Vue make more sense.

How do I debug OWL components?

Open the browser devtools, switch to dev mode in Odoo (?debug=1), and OWL provides a component tree inspector. The reactivity system logs trigger sources in the console when verbose logging is enabled. Standard JS debugger statements work as usual.

Does OWL support TypeScript?

Yes, increasingly so in newer Odoo versions. Types are provided for components, hooks, and services. Most Odoo core code is still JS, but you can write TypeScript modules and they compile via the asset pipeline.

What about server-side rendering?

OWL is a client-side framework. Odoo's website module uses QWeb (server-rendered) for public pages and OWL for editor and admin. There's no built-in OWL SSR; for SEO-critical content, use server-rendered QWeb templates.


OWL is the front-end framework you need for any production Odoo customization. ECOSIRE's Odoo developer hire service places engineers fluent in OWL who can ship custom widgets, screens, and integrations. See our Odoo customization service for module development including front-end work, or browse our Odoo modules catalog for examples of well-structured OWL components.

E

Yazan

ECOSIRE Team

Technical Writing

The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.

ECOSIRE

Odoo ERP ile İşinizi Dönüştürün

Operasyonlarınızı kolaylaştırmak için uzman Odoo uygulaması, özelleştirme ve destek.