[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.
This commit is contained in:
Géry Debongnie
2023-04-28 14:23:19 +02:00
committed by Sam Degueldre
parent 606d14a399
commit aabb7559b8
3 changed files with 78 additions and 1 deletions
+1 -1
View File
@@ -125,7 +125,7 @@ export class TemplateSet {
callTemplate(owner: any, subTemplate: string, ctx: any, parent: any, key: any): any { callTemplate(owner: any, subTemplate: string, ctx: any, parent: any, key: any): any {
const template = this.getTemplate(subTemplate); const template = this.getTemplate(subTemplate);
return toggler(subTemplate, template.call(owner, ctx, parent, key)); return toggler(subTemplate, template.call(owner, ctx, parent, key + subTemplate));
} }
} }
@@ -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`] = ` exports[`t-call dynamic t-call: key is propagated 1`] = `
"function anonymous(app, bdom, helpers "function anonymous(app, bdom, helpers
) { ) {
+27
View File
@@ -398,4 +398,31 @@ describe("t-call", () => {
}); });
expect(fixture.innerHTML).toBe(""); 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`
<t t-esc="current.template"/>
<t t-call="{{current.template}}"/>`;
static components = { Child };
current = useState({ template: "A" });
}
const root = await mount(Root, fixture, {
templates: `
<templates>
<t t-name="A"><Child/></t>
<t t-name="B"><Child/></t>
</templates>`,
});
expect(fixture.innerHTML).toBe("Achild");
root.current.template = "B";
await nextTick();
expect(fixture.innerHTML).toBe("Bchild");
});
}); });