diff --git a/doc/reference/component.md b/doc/reference/component.md index 00639b6e3..85b52712f 100644 --- a/doc/reference/component.md +++ b/doc/reference/component.md @@ -1068,6 +1068,8 @@ class App extends Component { In this example, the component `App` selects dynamically the concrete sub component class. +Note that the `t-component` directive can only be used on `` nodes. + ### Asynchronous Rendering Working with asynchronous code always adds a lot of complexity to a system. Whenever diff --git a/src/qweb/qweb.ts b/src/qweb/qweb.ts index 53cfe6359..250f27c49 100644 --- a/src/qweb/qweb.ts +++ b/src/qweb/qweb.ts @@ -460,6 +460,8 @@ export class QWeb extends EventBus { // this is a component, we modify in place the xml document to change // to node.setAttribute("t-component", node.tagName); + } else if (node.tagName !== 't' && node.hasAttribute('t-component')) { + throw new Error(`Directive 't-component' can only be used on nodes (used on a <${node.tagName}>)`); } const attributes = (node).attributes; diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts index 2b65b6dfb..3dcb7097d 100644 --- a/tests/component/component.test.ts +++ b/tests/component/component.test.ts @@ -1414,6 +1414,25 @@ describe("composition", () => { expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot(); }); + test("t-component not on a node", async () => { + class Child extends Component { + static template = xml`1`; + } + class Parent extends Component { + static components = { Child }; + static template = xml`
`; + } + const parent = new Parent(); + let error; + try { + await parent.mount(fixture); + } catch (e) { + error = e; + } + expect(error).toBeDefined(); + expect(error.message).toBe(`Directive 't-component' can only be used on nodes (used on a
)`); + }); + test("sub components, loops, and shouldUpdate", async () => { class ChildWidget extends Component { static template = xml``;