diff --git a/src/qweb/qweb.ts b/src/qweb/qweb.ts index 2de45442..0e006c5d 100644 --- a/src/qweb/qweb.ts +++ b/src/qweb/qweb.ts @@ -137,12 +137,26 @@ const UTILS: Utils = { } return result; }, + /** + * This method combines the current context with the variables defined in a + * scope for use in a slot. + * + * The implementation is kind of tricky because we want to preserve the + * prototype chain structure of the cloned result. So we need to traverse the + * prototype chain, cloning each level respectively. + */ combine(context, scope) { - const clone = Object.create(context); + let clone = context; + const scopeStack = []; while (!isComponent(scope)) { - Object.assign(clone, scope); + scopeStack.push(scope); scope = scope.__proto__; } + while (scopeStack.length) { + let scope = scopeStack.pop(); + clone = Object.create(clone); + Object.assign(clone, scope); + } return clone; }, shallowEqual, diff --git a/tests/component/slots.test.ts b/tests/component/slots.test.ts index 54297cdc..ca9f88e4 100644 --- a/tests/component/slots.test.ts +++ b/tests/component/slots.test.ts @@ -1303,4 +1303,71 @@ describe("t-slot directive", () => { document.querySelector("button").click(); await nextTick(); }); + + test("t-slot in recursive templates", async () => { + QWeb.registerTemplate( + "_test_recursive_template", + ` + + + + + + + + + + + + + + + ` + ); + + class Wrapper extends Component { + static template = xml` + + + `; + } + + class Parent extends Component { + static template = "_test_recursive_template"; + static components = { Wrapper }; + name = "foo"; + items = [ + { + name: "foo-0", + children: [ + { name: "foo-00", children: [] }, + { + name: "foo-01", + children: [ + { name: "foo-010", children: [] }, + { name: "foo-011", children: [] }, + { + name: "foo-012", + children: [ + { name: "foo-0120", children: [] }, + { name: "foo-0121", children: [] }, + { name: "foo-0122", children: [] }, + ], + }, + ], + }, + { name: "foo-02", children: [] }, + ], + }, + { name: "foo-1", children: [] }, + { name: "foo-2", children: [] }, + ]; + } + + await mount(Parent, { target: fixture }); + + expect(fixture.innerHTML).toBe( + "foofoo-0foo-00foo-01foo-010foo-011foo-012foo-0120foo-0121foo-0122foo-02foo-1foo-2" + ); + }); });