From 3bf91afc3f861b7d671b0d404a62cb451920ee4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 16 Sep 2020 09:58:57 +0200 Subject: [PATCH] [FIX] qweb: fix issue variables set in body of t-call The body of a t-call directive may be used to define private variables to the sub template call. However, the code that handles t-call worked like this: - compile sub template if necessary - then compile body of t-call to extract variables This means that the variables defined in the t-call body were not yet processed and available in the context. Because of that, when the call to t-esc is done, there is not internal qweb var, and the code simply outputs a scope['varname'], which is in our case a VDOMArray, so it is displayed as [object object] What this fix does is changing the way t-esc works: if we are in the context of a sub template, then it assumes that any outside variable may or may not be a VDomArray, so it needs to check and eventually convert it to a string, if necessary. closes #719 --- src/qweb/base_directives.ts | 8 +++++- src/qweb/compilation_context.ts | 1 + src/qweb/qweb.ts | 4 ++- tests/qweb/__snapshots__/qweb.test.ts.snap | 30 ++++++++++++++++++++++ tests/qweb/qweb.test.ts | 15 +++++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/qweb/base_directives.ts b/src/qweb/base_directives.ts index 95c9a3cd..e55180d2 100644 --- a/src/qweb/base_directives.ts +++ b/src/qweb/base_directives.ts @@ -41,6 +41,12 @@ function compileValueNode(value: any, node: Element, qweb: QWeb, ctx: Compilatio if (typeof value === "string") { exprID = `_${ctx.generateID()}`; ctx.addLine(`let ${exprID} = ${ctx.formatExpression(value)};`); + if (ctx.isSubTemplate) { + ctx.rootContext.shouldDefineUtils = true; + ctx.addLine( + `${exprID} = ${exprID} instanceof utils.VDomArray ? utils.vDomToString(${exprID}) : ${exprID};` + ); + } } else { exprID = `scope.${value.id}`; } @@ -238,7 +244,7 @@ QWeb.addDirective({ if (!subId) { subId = QWeb.nextId++; qweb.subTemplates[subTemplate] = subId; - const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx, true); + const subTemplateFn = qweb._compile(subTemplate, nodeTemplate.elem, ctx, true, true); QWeb.subTemplates[subId] = subTemplateFn; } diff --git a/src/qweb/compilation_context.ts b/src/qweb/compilation_context.ts index 38f0c46b..1996b0d9 100644 --- a/src/qweb/compilation_context.ts +++ b/src/qweb/compilation_context.ts @@ -22,6 +22,7 @@ export class CompilationContext { shouldDefineUtils: boolean = false; shouldDefineRefs: boolean = false; shouldDefineResult: boolean = true; + isSubTemplate: boolean = false; loopNumber: number = 0; inPreTag: boolean = false; templateName: string; diff --git a/src/qweb/qweb.ts b/src/qweb/qweb.ts index d9e6b067..cc5e54f7 100644 --- a/src/qweb/qweb.ts +++ b/src/qweb/qweb.ts @@ -413,10 +413,12 @@ export class QWeb extends EventBus { name: string, elem: Element, parentContext?: CompilationContext, - defineKey?: boolean + defineKey?: boolean, + isSubTemplate?: boolean ): CompiledTemplate { const isDebug = elem.attributes.hasOwnProperty("t-debug"); const ctx = new CompilationContext(name); + ctx.isSubTemplate = Boolean(isSubTemplate); if (elem.tagName !== "t") { ctx.shouldDefineResult = false; } diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap index 93446685..a85b43d6 100644 --- a/tests/qweb/__snapshots__/qweb.test.ts.snap +++ b/tests/qweb/__snapshots__/qweb.test.ts.snap @@ -1502,6 +1502,7 @@ exports[`t-call (template calling recursive template, part 2 2`] = ` let vn3 = h('p', p3, c3); c2.push(vn3); let _4 = scope['node'].val; + _4 = _4 instanceof utils.VDomArray ? utils.vDomToString(_4) : _4; if (_4 != null) { c3.push({text: _4}); } @@ -1581,6 +1582,7 @@ exports[`t-call (template calling recursive template, part 3 2`] = ` let vn3 = h('p', p3, c3); c2.push(vn3); let _4 = scope['node'].val; + _4 = _4 instanceof utils.VDomArray ? utils.vDomToString(_4) : _4; if (_4 != null) { c3.push({text: _4}); } @@ -1662,6 +1664,7 @@ exports[`t-call (template calling recursive template, part 4: with t-set recursi let vn3 = h('p', p3, c3); c2.push(vn3); let _4 = scope['node'].val; + _4 = _4 instanceof utils.VDomArray ? utils.vDomToString(_4) : _4; if (_4 != null) { c3.push({text: _4}); } @@ -1752,6 +1755,33 @@ exports[`t-call (template calling t-call with t-if 1`] = ` }" `; +exports[`t-call (template calling t-call with t-set inside and body text content 1`] = ` +"function anonymous(context, extra +) { + // Template name: \\"main\\" + let utils = this.constructor.utils; + let scope = Object.create(context); + let h = this.h; + let c1 = [], p1 = {key:1}; + let vn1 = h('div', p1, c1); + let _origScope4 = scope; + scope = Object.create(scope); + scope.__access_mode__ = 'ro'; + { + { + let c__0 = []; + let c5 = new utils.VDomArray(); + c5.push({text: \`yip yip\`}); + scope.val = c5 + scope[utils.zero] = c__0; + } + this.constructor.subTemplates['1'].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__6__'})); + } + scope = _origScope4; + return vn1; +}" +`; + exports[`t-call (template calling t-call with t-set inside and outside 1`] = ` "function anonymous(context, extra ) { diff --git a/tests/qweb/qweb.test.ts b/tests/qweb/qweb.test.ts index 4da96e38..356deaf3 100644 --- a/tests/qweb/qweb.test.ts +++ b/tests/qweb/qweb.test.ts @@ -1072,6 +1072,21 @@ describe("t-call (template calling", () => { expect(renderToString(qweb1, "main")).toBe("
ok
"); expect(renderToString(qweb2, "main")).toBe("
ok
"); }); + + test("t-call with t-set inside and body text content", () => { + qweb.addTemplate("sub", `

`); + qweb.addTemplate( + "main", + ` +
+ + yip yip + +
` + ); + const expected = "

yip yip

"; + expect(renderToString(qweb, "main")).toBe(expected); + }); }); describe("foreach", () => {