mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] qweb: allow string interpolation in t-call
This commit allows the t-call directive to choose a dynamic template. This is working in QWeb Python, but was not possible in Owl. closes #709
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
- [Dynamic Attributes](#dynamic-attributes)
|
- [Dynamic Attributes](#dynamic-attributes)
|
||||||
- [Loops](#loops)
|
- [Loops](#loops)
|
||||||
- [Rendering Sub Templates](#rendering-sub-templates)
|
- [Rendering Sub Templates](#rendering-sub-templates)
|
||||||
|
- [Dynamic Sub Templates](#dynamic-sub-templates)
|
||||||
- [Translations](#translations)
|
- [Translations](#translations)
|
||||||
- [Debugging](#debugging)
|
- [Debugging](#debugging)
|
||||||
|
|
||||||
@@ -509,6 +510,22 @@ This can be used to define variables scoped to a sub template:
|
|||||||
<!-- "var" does not exist here -->
|
<!-- "var" does not exist here -->
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Dynamic sub templates
|
||||||
|
|
||||||
|
The `t-call` directive can also be used to dynamically call a sub template,
|
||||||
|
using string interpolation. For example:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<div t-name="main-template">
|
||||||
|
<t t-call="{{template}}">
|
||||||
|
<em>content</em>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, the name of the template is obtained from the `template` value in the
|
||||||
|
template rendering context.
|
||||||
|
|
||||||
### Translations
|
### Translations
|
||||||
|
|
||||||
By default, QWeb specify that templates should be translated. If this behaviour
|
By default, QWeb specify that templates should be translated. If this behaviour
|
||||||
|
|||||||
+26
-10
@@ -1,4 +1,4 @@
|
|||||||
import { CompilationContext } from "./compilation_context";
|
import { CompilationContext, INTERP_REGEXP } from "./compilation_context";
|
||||||
import { QWeb } from "./qweb";
|
import { QWeb } from "./qweb";
|
||||||
import { htmlToVDOM } from "../vdom/html_to_vdom";
|
import { htmlToVDOM } from "../vdom/html_to_vdom";
|
||||||
import { QWebVar } from "./expression_parser";
|
import { QWebVar } from "./expression_parser";
|
||||||
@@ -224,19 +224,35 @@ QWeb.addDirective({
|
|||||||
ctx.rootContext.shouldDefineScope = true;
|
ctx.rootContext.shouldDefineScope = true;
|
||||||
ctx.rootContext.shouldDefineUtils = true;
|
ctx.rootContext.shouldDefineUtils = true;
|
||||||
const subTemplate = node.getAttribute("t-call")!;
|
const subTemplate = node.getAttribute("t-call")!;
|
||||||
|
const isDynamic = INTERP_REGEXP.test(subTemplate);
|
||||||
const nodeTemplate = qweb.templates[subTemplate];
|
const nodeTemplate = qweb.templates[subTemplate];
|
||||||
if (!nodeTemplate) {
|
if (!isDynamic && !nodeTemplate) {
|
||||||
throw new Error(`Cannot find template "${subTemplate}" (t-call)`);
|
throw new Error(`Cannot find template "${subTemplate}" (t-call)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: compile target template in sub templates
|
// Step 2: compile target template in sub templates
|
||||||
// ------------------------------------------------
|
// ------------------------------------------------
|
||||||
let subId = qweb.subTemplates[subTemplate];
|
let subIdstr: string;
|
||||||
if (!subId) {
|
if (isDynamic) {
|
||||||
subId = QWeb.nextId++;
|
const _id = ctx.generateID();
|
||||||
qweb.subTemplates[subTemplate] = subId;
|
ctx.addLine(`let tname${_id} = ${ctx.interpolate(subTemplate)};`);
|
||||||
const subTemplateFn = qweb._compile(subTemplate, { hasParent: true, defineKey: true });
|
ctx.addLine(`let tid${_id} = this.constructor.nextId++;`);
|
||||||
QWeb.subTemplates[subId] = subTemplateFn;
|
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
|
// Step 3: compile t-call body if necessary
|
||||||
@@ -272,12 +288,12 @@ QWeb.addDirective({
|
|||||||
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
|
const parentNode = ctx.parentNode ? `c${ctx.parentNode}` : "result";
|
||||||
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`;
|
const extra = `Object.assign({}, extra, {parentNode: ${parentNode}, parent: ${parentComponent}, key: ${key}})`;
|
||||||
if (ctx.parentNode) {
|
if (ctx.parentNode) {
|
||||||
ctx.addLine(`this.constructor.subTemplates['${subId}'].call(this, scope, ${extra});`);
|
ctx.addLine(`this.constructor.subTemplates[${subIdstr}].call(this, scope, ${extra});`);
|
||||||
} else {
|
} else {
|
||||||
// this is a t-call with no parentnode, we need to extract the result
|
// this is a t-call with no parentnode, we need to extract the result
|
||||||
ctx.rootContext.shouldDefineResult = true;
|
ctx.rootContext.shouldDefineResult = true;
|
||||||
ctx.addLine(`result = []`);
|
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]`);
|
ctx.addLine(`result = result[0]`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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`] = `
|
exports[`t-call (template calling inherit context 1`] = `
|
||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1095,6 +1095,16 @@ describe("t-call (template calling", () => {
|
|||||||
const expected = "<div><p>yip yip</p></div>";
|
const expected = "<div><p>yip yip</p></div>";
|
||||||
expect(renderToString(qweb, "main")).toBe(expected);
|
expect(renderToString(qweb, "main")).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("dynamic t-call", () => {
|
||||||
|
qweb.addTemplate("foo", `<foo><t t-esc="val"/></foo>`);
|
||||||
|
qweb.addTemplate("bar", `<bar><t t-esc="val"/></bar>`);
|
||||||
|
qweb.addTemplate("main", `<div><t t-call="{{template}}"/></div>`);
|
||||||
|
const expected = "<div><foo>foo</foo></div>";
|
||||||
|
expect(renderToString(qweb, "main", { template: "foo", val: "foo" })).toBe(expected);
|
||||||
|
const expected2 = "<div><bar>quux</bar></div>";
|
||||||
|
expect(renderToString(qweb, "main", { template: "bar", val: "quux" })).toBe(expected2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("foreach", () => {
|
describe("foreach", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user