Skip to Content
āđ€āļĄāļ™āļđ
āļ„āļļāļ“āļ•āđ‰āļ­āļ‡āļĨāļ‡āļ—āļ°āđ€āļšāļĩāļĒāļ™āđ€āļžāļ·āđˆāļ­āđ‚āļ•āđ‰āļ•āļ­āļšāļāļąāļšāļ„āļ­āļĄāļĄāļđāļ™āļīāļ•āļĩāđ‰
āļ„āļģāļ–āļēāļĄāļ™āļĩāđ‰āļ–āļđāļāļ•āļąāđ‰āļ‡āļ„āđˆāļēāļŠāļ–āļēāļ™āļ°
1 āļ•āļ­āļšāļāļĨāļąāļš
749 āļĄāļļāļĄāļĄāļ­āļ‡

🔧 OWL Component Not Rendering in Custom Layout (Odoo 18) — Likely XML Template Issue

Hello everyone,

I'm currently working on Odoo 18 and trying to render a simple OWL component inside a custom dashboard layout. Despite successfully loading all related JS files, the component does not appear in the UI, and I suspect the issue lies in how the XML template is handled.

📁 Project Structure

My layout is defined in:

my_module/static/src/views/dashboard_page_layout.xml

My JS files are located in:

my_module/static/src/js/

ðŸ“Ķ Files

HelloBox.js

/** @odoo-module **/

import { Component, xml } from '@odoo/owl';

export class HelloBox extends Component {}

HelloBox.template = xml`
  <div class="hello-box">
    <h1><t t-esc="props.title"/></h1>
    <button t-on-click="sayHello">Click me</button>
  </div>
`;

HelloBox.props = {
  title: { type: String },
};

HelloBox.prototype.sayHello = function () {
  alert('Hello from OWL!');
};

HelloBox.xml

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
  <t t-name="my_module.HelloBox" owl="1">
    <div class="p-3">
      <p><t t-out="props.title"/></p>
      <button>Click me</button>
    </div>
  </t>
</templates>

init_owl.js

/** @odoo-module **/

import { mount, whenReady } from '@odoo/owl';
import { HelloBox } from './HelloBox';

whenReady(() => {
  const root = document.getElementById('hello-box-root');
  console.log('Mounting HelloBox in:', root);

  if (root) {
    mount(HelloBox, {
      target: root,
      props: { title: 'Witaj z OWL!' },
    });
  }
});

In my dashboard_page_layout.xml, I manually placed:

<div id="hello-box-root"></div>

ðŸ§Đ Problem Focus

✅ The JS loads successfully, console.log confirms the mount target exists, and the component code is correct.

❌ The XML template seems to be the weak point — I suspect it may not be loaded or registered correctly, despite being included in the assets.

In some cases I even get:

TypeError: Cannot read properties of undefined (reading 'defaultProps')

Which usually indicates OWL is trying to mount an incomplete or undefined component — possibly due to the XML template not being properly compiled or injected.

📜 __manifest__.py

'assets': {
    'web.assets_frontend': [
        'my_module/static/src/js/HelloBox.xml',
        'my_module/static/src/js/HelloBox.js',
        'my_module/static/src/js/init_owl.js',
    ],
},

I've also tried adding the XML to web.assets_qweb, web.assets_backend, and changing the load order, but the issue remains.

🙏 What I'm Looking For

  • How should XML templates be properly registered in OWL/Odoo 18 when working with web.assets_frontend?
  • Do I need to explicitly register or reference them in a different way?
  • Is there anything missing to make this work with t-name="my_module.HelloBox"?
  • Is using xml\template`` inside the JS file the preferred approach instead?

Any guidance would be highly appreciated.

Thanks in advance for your support!

Karol


āļ­āļ§āļ•āļēāļĢ
āļĨāļ°āļ—āļīāđ‰āļ‡
āļœāļđāđ‰āđ€āļ‚āļĩāļĒāļ™


Unfortunately, that’s not the case. While I’ve successfully used inline XML templates, that’s not the approach I want to take. I’d like to define my OWL templates in separate XML files so that I can pass props and create dynamic, reusable components.

ðŸ§Đ Template File: owl_templates.xml

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
  <t t-name="my_folder.owl_main_component" owl="1">
    <div>
      <h3>This is rendered using OWL</h3>
    </div>
    <!-- I intentionally commented out the <t> tag earlier to test loading -->
  </t>
</templates>

🧠 Component File: owl_component.js

/** @odoo-module **/

import { Component, mount, xml, whenReady } from '@odoo/owl';
import { templates } from '@web/core/assets';

export class OwlMainComponent extends Component {}

OwlMainComponent.template = 'my_folder.owl_main_component';

// VERSION 1: await template loading inside whenReady
whenReady(async () => {
	await templates;
	const element = document.getElementById('hello-box-root');
	if (element) {
		mount(OwlMainComponent, element);
	} else {
		console.warn('❌ Missing #hello-box-root');
	}
});

// VERSION 2: naive sync mount
// const element = document.getElementById('hello-box-root');
// mount(OwlMainComponent, element);

// VERSION 3: basic whenReady
// whenReady(() => {
// 	const element = document.getElementById('hello-box-root');
// 	if (element) {
// 		mount(OwlMainComponent, element, { templates });
// 	}
// });

🔍 Investigation So Far

When I intentionally break the XML file (e.g. by removing a <t>), Odoo correctly throws an error — which proves the XML is found and parsed.

I also inspected the compiled web.assets_frontend_lazy.js. It turns out:

  • The component is mounted around line 97278
  • The XML template is only registered around line 104900

This confirms the component tries to render before the template is available.

🧊 Asset Load Order (Manifest)

To fix this, I tried forcing the load order using before and prepend:

'web.assets_frontend': [
    # ('prepend', 'my_folder/static/src/js/owl_templates.xml'),
    # ('before', 'my_folder/static/src/js/owl_component.js',
    #           'my_folder/static/src/js/owl_templates.xml'),
    ('before', 'my_folder/static/src/js/owl_templates.xml', 
               'my_folder/static/src/js/owl_component.js'),
]

However, every attempt resulted in an error saying either the XML or JS file could not be found — depending on which directive I used.

🆘 Summary

At this point I’ve tried every variation I could think of. It’s very frustrating that something as basic as loading a QWeb XML file into an OWL component doesn't work as expected.

If anyone has had success with a similar setup or knows what I might be doing wrong — I’d deeply appreciate your help 🙏

āļ„āļģāļ•āļ­āļšāļ—āļĩāđˆāļ”āļĩāļ—āļĩāđˆāļŠāļļāļ”

Hii

__manifest__.py

{ "name": "Custom OWL Dashboard", "version": "1.0", "depends": ["base", "web"], "assets": { "web.assets_frontend": [ "my_module/static/src/js/HelloBox.js", "my_module/static/src/js/init_owl.js", ], }, "data": [ "views/dashboard_menu.xml", ], "installable": True, "application": True, }

HelloBox.js

my_module/static/src/js/HelloBox.js

/** @odoo -module **/ import { Component, xml } from "@odoo/owl"; export class HelloBox extends Component { static template = xml` <div class="hello-box bg-light p-3 rounded shadow"> <h2 class="mb-3"><t t-esc="props.title"/></h2> <button class="btn btn-primary" t-on-click="sayHello">Click me</button> </div> `; static props = { title: { type: String }, }; sayHello() { alert("Hello from OWL!"); } }

init_owl.js

my_module/static/src/js/init_owl.js

/** @odoo -module **/ import { mount, whenReady } from "@odoo/owl"; import { HelloBox } from "./HelloBox"; whenReady(() => { const root = document.getElementById("hello-box-root"); console.log("ðŸ“Ķ Mounting HelloBox into", root); if (root) { mount(HelloBox, { target: root, props: { title: "Hello from OWL!", }, }); } else { console.warn("⚠ïļ Element #hello-box-root not found."); } });

dashboard_menu.xml

my_module/views/dashboard_menu.xml


<odoo> <template id="assets_backend" inherit_id="web.assets_backend" name="OWL Dashboard Assets"> <xpath expr="." position="inside"> <link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.min.css"/> </xpath> </template> <record id="action_dashboard_hello" model="ir.actions.client"> <field name="name">OWL Dashboard</field> <field name="tag">hello_dashboard</field> </record> <menuitem id="menu_owl_dashboard_root" name="OWL Dashboard" sequence="10"/> <menuitem id="menu_owl_dashboard" parent="menu_owl_dashboard_root" name="Hello" action="action_dashboard_hello"/> </odoo>

JS Controller (Frontend Mount)

You need to register your OWL component on that page. Add this file:

my_module/static/src/js/dashboard_controller.js (optional if you're using OWL directly)

If you're using the tag 'hello_dashboard', register it using a JS client action — or skip that if you're directly including the root container via another QWeb view.

i hope it is usefull

āļ­āļ§āļ•āļēāļĢ
āļĨāļ°āļ—āļīāđ‰āļ‡
Related Posts āļ•āļ­āļšāļāļĨāļąāļš āļĄāļļāļĄāļĄāļ­āļ‡ āļāļīāļˆāļāļĢāļĢāļĄ
2
āļ.āļ„. 25
426
2
āļ.āļž. 25
1104
4
āļŠ.āļ„. 25
1104
0
āļĄāļī.āļĒ. 25
506
1
āļĄāļī.āļĒ. 25
829