From aabb7559b8c9d1fca274f4992fd877f81c266a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Fri, 28 Apr 2023 14:23:19 +0200 Subject: [PATCH] [FIX] components: properly differentiate t-call subcomponents Before this commit, when using a dynamic t-call, it was possible to have a collision between subcomponent keys. This would cause hard to understand owl crash, because owl would incorrectly use the same component instance for two different template location. From owl point of view, one of these component has to be destroyed and the other one should be mounted, so a crash would occur. The fix is to simply properly key sub components. --- src/runtime/template_set.ts | 2 +- .../__snapshots__/t_call.test.ts.snap | 50 +++++++++++++++++++ tests/components/t_call.test.ts | 27 ++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/runtime/template_set.ts b/src/runtime/template_set.ts index 6868e612..83bc4450 100644 --- a/src/runtime/template_set.ts +++ b/src/runtime/template_set.ts @@ -125,7 +125,7 @@ export class TemplateSet { callTemplate(owner: any, subTemplate: string, ctx: any, parent: any, key: any): any { const template = this.getTemplate(subTemplate); - return toggler(subTemplate, template.call(owner, ctx, parent, key)); + return toggler(subTemplate, template.call(owner, ctx, parent, key + subTemplate)); } } diff --git a/tests/components/__snapshots__/t_call.test.ts.snap b/tests/components/__snapshots__/t_call.test.ts.snap index 31284ce1..b5825bdc 100644 --- a/tests/components/__snapshots__/t_call.test.ts.snap +++ b/tests/components/__snapshots__/t_call.test.ts.snap @@ -42,6 +42,56 @@ exports[`t-call dynamic t-call 3`] = ` }" `; +exports[`t-call dynamic t-call with same sub component 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const call = app.callTemplate.bind(app); + + return function template(ctx, node, key = \\"\\") { + const b2 = text(ctx['current'].template); + const template1 = (ctx['current'].template); + const b3 = call(this, template1, ctx, node, key + \`__1\`); + return multi([b2, b3]); + } +}" +`; + +exports[`t-call dynamic t-call with same sub component 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const comp1 = app.createComponent(\`Child\`, true, false, false, []); + + return function template(ctx, node, key = \\"\\") { + return comp1({}, key + \`__1\`, node, this, null); + } +}" +`; + +exports[`t-call dynamic t-call with same sub component 3`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`child\`); + } +}" +`; + +exports[`t-call dynamic t-call with same sub component 4`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const comp1 = app.createComponent(\`Child\`, true, false, false, []); + + return function template(ctx, node, key = \\"\\") { + return comp1({}, key + \`__1\`, node, this, null); + } +}" +`; + exports[`t-call dynamic t-call: key is propagated 1`] = ` "function anonymous(app, bdom, helpers ) { diff --git a/tests/components/t_call.test.ts b/tests/components/t_call.test.ts index 3e70dd6e..2080560e 100644 --- a/tests/components/t_call.test.ts +++ b/tests/components/t_call.test.ts @@ -398,4 +398,31 @@ describe("t-call", () => { }); expect(fixture.innerHTML).toBe(""); }); + + test("dynamic t-call with same sub component", async () => { + class Child extends Component { + static template = xml`child`; + } + + class Root extends Component { + static template = xml` + + `; + static components = { Child }; + current = useState({ template: "A" }); + } + + const root = await mount(Root, fixture, { + templates: ` + + + + `, + }); + expect(fixture.innerHTML).toBe("Achild"); + + root.current.template = "B"; + await nextTick(); + expect(fixture.innerHTML).toBe("Bchild"); + }); });