From ea3c6f7bf01d0642b2539e0dbe011aabdff3557a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 7 Jul 2021 10:31:37 +0200 Subject: [PATCH] [FIX] slots: make sure handlers are properly bound to component In some situations (a slot inside a slot), the combine method was wrongly copying all properties of the scope into the context, which caused the event handling system to wrongly use a subobject as component (since it detects the fact that __owl__ is a own property_). Consequently, we could have very subtle issue with some properties being shadowed by a sub object. --- src/qweb/qweb.ts | 5 ++-- tests/component/slots.test.ts | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/qweb/qweb.ts b/src/qweb/qweb.ts index 2edc3b0c..2de45442 100644 --- a/src/qweb/qweb.ts +++ b/src/qweb/qweb.ts @@ -139,8 +139,9 @@ const UTILS: Utils = { }, combine(context, scope) { const clone = Object.create(context); - for (let k in scope) { - clone[k] = scope[k]; + while (!isComponent(scope)) { + Object.assign(clone, scope); + scope = scope.__proto__; } return clone; }, diff --git a/tests/component/slots.test.ts b/tests/component/slots.test.ts index b95bcaae..54297cdc 100644 --- a/tests/component/slots.test.ts +++ b/tests/component/slots.test.ts @@ -1257,4 +1257,50 @@ describe("t-slot directive", () => { `
` ); }); + + test("t-slot scope context", async () => { + expect.assertions(1); + + class Wrapper extends Component { + static template = xml``; + } + + let dialog; + + class Dialog extends Component { + static template = xml` + +
+ +
+
+ `; + + static components = { Wrapper }; + + setup() { + dialog = this; + } + + onClick(ev) { + // we do not use expect(this).toBe(dialog) here because if it fails, it + // may blow up jest because it then tries to compute a diff, which is + // infinite if there is a cycle + expect(this === dialog).toBe(true); + } + } + + class Parent extends Component { + static template = xml` + + + `; + static components = { Dialog }; + } + + await mount(Parent, { target: fixture }); + + document.querySelector("button").click(); + await nextTick(); + }); });