diff --git a/src/qweb/base_directives.ts b/src/qweb/base_directives.ts index 463edcaa..b8b10a3c 100644 --- a/src/qweb/base_directives.ts +++ b/src/qweb/base_directives.ts @@ -28,9 +28,12 @@ QWeb.utils.getFragment = function(str: string): DocumentFragment { QWeb.utils.htmlToVDOM = htmlToVDOM; function compileValueNode(value: any, node: Element, qweb: QWeb, ctx: CompilationContext) { - if (value === "0" && ctx.caller) { - qweb._compileNode(ctx.caller, ctx); - return; + if (value === "0") { + const caller = ctx.getCaller(); + if (caller) { + qweb._compileNode(caller, ctx.getInliningContext()); + return; + } } if (value.xml instanceof NodeList && !value.id) { diff --git a/src/qweb/compilation_context.ts b/src/qweb/compilation_context.ts index 45189cc3..6fe8b319 100644 --- a/src/qweb/compilation_context.ts +++ b/src/qweb/compilation_context.ts @@ -32,6 +32,8 @@ export class CompilationContext { scopeVars: any[] = []; currentKey: string = ""; templates: { [key: string]: boolean } = {}; + callingLevel: number = 0; + inliningLevel: number = 0; constructor(name?: string) { this.rootContext = this; @@ -129,6 +131,10 @@ export class CompilationContext { subContext(key: keyof CompilationContext, value: any): CompilationContext { const newContext = Object.create(this); newContext[key] = value; + if (key === 'caller') { + newContext.callingLevel++; + newContext.inliningLevel++; + } return newContext; } @@ -166,6 +172,27 @@ export class CompilationContext { this.dedent(); this.addLine("}"); } + /** + * Recursively (inverse) fetches the `caller` of a context + * Useful to determine to which t-call a t-raw="0" refers + */ + getCaller(targetLevel?: number): Element | null { + if (targetLevel === undefined) { + targetLevel = this.inliningLevel; + } + if (targetLevel === this.callingLevel) { + return this.caller || null; + } + const proto = (this as any).__proto__; + return proto ? proto.getCaller(targetLevel) : null; + } + /** + * Marks the context with the current recursive level + * in which we are for inlining archs (t-raw="0") + */ + getInliningContext(): CompilationContext { + return this.subContext('inliningLevel', this.inliningLevel - 1); + } getValue(val: any): QWebVar | string { return val in this.variables ? this.getValue(this.variables[val]) : val; diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap index 409cc823..14ac1876 100644 --- a/tests/qweb/__snapshots__/qweb.test.ts.snap +++ b/tests/qweb/__snapshots__/qweb.test.ts.snap @@ -910,6 +910,46 @@ exports[`t-call (template calling call with several sub nodes on same line 1`] = }" `; +exports[`t-call (template calling cascading t-call t-raw='0' 1`] = ` +"function anonymous(context,extra +) { + var h = this.h; + let c1 = [], p1 = {key:1}; + var vn1 = h('div', p1, c1); + let c5 = [], p5 = {key:5}; + var vn5 = h('div', p5, c5); + c1.push(vn5); + let c8 = [], p8 = {key:8}; + var vn8 = h('div', p8, c8); + c5.push(vn8); + let c11 = [], p11 = {key:11}; + var vn11 = h('div', p11, c11); + c8.push(vn11); + let c12 = [], p12 = {key:12}; + var vn12 = h('span', p12, c12); + c11.push(vn12); + c12.push({text: \`cascade 2\`}); + let c13 = [], p13 = {key:13}; + var vn13 = h('span', p13, c13); + c11.push(vn13); + c13.push({text: \`cascade 1\`}); + let c14 = [], p14 = {key:14}; + var vn14 = h('span', p14, c14); + c11.push(vn14); + c14.push({text: \`cascade 0\`}); + let c15 = [], p15 = {key:15}; + var vn15 = h('span', p15, c15); + c11.push(vn15); + c15.push({text: \`hey\`}); + c11.push({text: \` \`}); + let c16 = [], p16 = {key:16}; + var vn16 = h('span', p16, c16); + c11.push(vn16); + c16.push({text: \`yay\`}); + return vn1; +}" +`; + exports[`t-call (template calling inherit context 1`] = ` "function anonymous(context,extra ) { diff --git a/tests/qweb/qweb.test.ts b/tests/qweb/qweb.test.ts index d1d85c4b..6a45f66d 100644 --- a/tests/qweb/qweb.test.ts +++ b/tests/qweb/qweb.test.ts @@ -673,6 +673,39 @@ describe("t-call (template calling", () => { expect(renderToString(qweb, "main")).toBe(expected); }); + test("cascading t-call t-raw='0'", () => { + qweb.addTemplates(` + +
+ cascade 2 + +
+ +
+ + cascade 1 + + +
+ +
+ + cascade 0 + + +
+ +
+ + hey yay + +
+
+ `); + const expected = "
cascade 2cascade 1cascade 0hey yay
"; + expect(renderToString(qweb, "main")).toBe(expected); + }); + test("recursive template, part 1", () => { qweb.addTemplates(`