diff --git a/doc/reference/qweb_templating_language.md b/doc/reference/qweb_templating_language.md
index 81cd6378..b1d910c5 100644
--- a/doc/reference/qweb_templating_language.md
+++ b/doc/reference/qweb_templating_language.md
@@ -15,6 +15,7 @@
- [Dynamic Attributes](#dynamic-attributes)
- [Loops](#loops)
- [Rendering Sub Templates](#rendering-sub-templates)
+ - [Dynamic Sub Templates](#dynamic-sub-templates)
- [Translations](#translations)
- [Debugging](#debugging)
@@ -509,6 +510,22 @@ This can be used to define variables scoped to a sub template:
```
+### Dynamic sub templates
+
+The `t-call` directive can also be used to dynamically call a sub template,
+using string interpolation. For example:
+
+```xml
+
+
+ content
+
+
+```
+
+Here, the name of the template is obtained from the `template` value in the
+template rendering context.
+
### Translations
By default, QWeb specify that templates should be translated. If this behaviour
diff --git a/src/qweb/base_directives.ts b/src/qweb/base_directives.ts
index b6f23e9c..aa14bf9a 100644
--- a/src/qweb/base_directives.ts
+++ b/src/qweb/base_directives.ts
@@ -1,4 +1,4 @@
-import { CompilationContext } from "./compilation_context";
+import { CompilationContext, INTERP_REGEXP } from "./compilation_context";
import { QWeb } from "./qweb";
import { htmlToVDOM } from "../vdom/html_to_vdom";
import { QWebVar } from "./expression_parser";
@@ -224,19 +224,35 @@ QWeb.addDirective({
ctx.rootContext.shouldDefineScope = true;
ctx.rootContext.shouldDefineUtils = true;
const subTemplate = node.getAttribute("t-call")!;
+ const isDynamic = INTERP_REGEXP.test(subTemplate);
const nodeTemplate = qweb.templates[subTemplate];
- if (!nodeTemplate) {
+ if (!isDynamic && !nodeTemplate) {
throw new Error(`Cannot find template "${subTemplate}" (t-call)`);
}
// Step 2: compile target template in sub templates
// ------------------------------------------------
- let subId = qweb.subTemplates[subTemplate];
- if (!subId) {
- subId = QWeb.nextId++;
- qweb.subTemplates[subTemplate] = subId;
- const subTemplateFn = qweb._compile(subTemplate, { hasParent: true, defineKey: true });
- QWeb.subTemplates[subId] = subTemplateFn;
+ let subIdstr: string;
+ if (isDynamic) {
+ const _id = ctx.generateID();
+ ctx.addLine(`let tname${_id} = ${ctx.interpolate(subTemplate)};`);
+ ctx.addLine(`let tid${_id} = this.constructor.nextId++;`);
+ ctx.addIf(`!(tname${_id} in this.subTemplates)`);
+ ctx.addLine(`this.subTemplates[tname${_id}] = tid${_id};`);
+ ctx.addLine(
+ `this.constructor.subTemplates[tid${_id}] = this._compile(tname${_id}, {hasParent: true, defineKey: true});`
+ );
+ ctx.closeIf();
+ subIdstr = `tid${_id}`;
+ } else {
+ let subId = qweb.subTemplates[subTemplate];
+ if (!subId) {
+ subId = QWeb.nextId++;
+ qweb.subTemplates[subTemplate] = subId;
+ const subTemplateFn = qweb._compile(subTemplate, { hasParent: true, defineKey: true });
+ QWeb.subTemplates[subId] = subTemplateFn;
+ }
+ subIdstr = `'${subId}'`;
}
// Step 3: compile t-call body if necessary
@@ -272,12 +288,12 @@ QWeb.addDirective({
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`;
if (ctx.parentNode) {
- ctx.addLine(`this.constructor.subTemplates['${subId}'].call(this, scope, ${extra});`);
+ ctx.addLine(`this.constructor.subTemplates[${subIdstr}].call(this, scope, ${extra});`);
} else {
// this is a t-call with no parentnode, we need to extract the result
ctx.rootContext.shouldDefineResult = true;
ctx.addLine(`result = []`);
- ctx.addLine(`this.constructor.subTemplates['${subId}'].call(this, scope, ${extra});`);
+ ctx.addLine(`this.constructor.subTemplates[${subIdstr}].call(this, scope, ${extra});`);
ctx.addLine(`result = result[0]`);
}
diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap
index b7f33abf..4392f539 100644
--- a/tests/qweb/__snapshots__/qweb.test.ts.snap
+++ b/tests/qweb/__snapshots__/qweb.test.ts.snap
@@ -1391,6 +1391,54 @@ exports[`t-call (template calling cascading t-call t-raw='0' 1`] = `
}"
`;
+exports[`t-call (template calling dynamic t-call 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 tname2 = (scope['template']);
+ let tid2 = this.constructor.nextId++;
+ if (!(tname2 in this.subTemplates)) {
+ this.subTemplates[tname2] = tid2;
+ this.constructor.subTemplates[tid2] = this._compile(tname2, {hasParent: true, defineKey: true});
+ }
+ let _origScope3 = scope;
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
+ this.constructor.subTemplates[tid2].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
+ scope = _origScope3;
+ return vn1;
+}"
+`;
+
+exports[`t-call (template calling dynamic t-call 2`] = `
+"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 tname2 = (scope['template']);
+ let tid2 = this.constructor.nextId++;
+ if (!(tname2 in this.subTemplates)) {
+ this.subTemplates[tname2] = tid2;
+ this.constructor.subTemplates[tid2] = this._compile(tname2, {hasParent: true, defineKey: true});
+ }
+ let _origScope3 = scope;
+ scope = Object.create(scope);
+ scope.__access_mode__ = 'ro';
+ this.constructor.subTemplates[tid2].call(this, scope, Object.assign({}, extra, {parentNode: c1, parent: utils.getComponent(context), key: '__4__'}));
+ scope = _origScope3;
+ 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 95c0d2c6..24b6908e 100644
--- a/tests/qweb/qweb.test.ts
+++ b/tests/qweb/qweb.test.ts
@@ -1095,6 +1095,16 @@ describe("t-call (template calling", () => {
const expected = "";
expect(renderToString(qweb, "main")).toBe(expected);
});
+
+ test("dynamic t-call", () => {
+ qweb.addTemplate("foo", ``);
+ qweb.addTemplate("bar", ``);
+ qweb.addTemplate("main", `
`);
+ const expected = "foo
";
+ expect(renderToString(qweb, "main", { template: "foo", val: "foo" })).toBe(expected);
+ const expected2 = "quux
";
+ expect(renderToString(qweb, "main", { template: "bar", val: "quux" })).toBe(expected2);
+ });
});
describe("foreach", () => {