From 26f14180e24a777ac2d77945273306d4992ed942 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Tue, 28 May 2019 13:17:38 +0200 Subject: [PATCH] [IMP] qweb: t-ref: dyn values with string interpolation Closes #100 --- doc/qweb.md | 22 ++++++++++++++-- extras/playground/samples.js | 2 +- extras/playground/templates.xml | 6 ++--- src/qweb_core.ts | 37 +++++++++++++++++++-------- src/qweb_extensions.ts | 4 +-- tests/__snapshots__/qweb.test.ts.snap | 10 ++++---- tests/animations.test.ts | 2 +- tests/component.test.ts | 12 ++++----- tests/qweb.test.ts | 15 ++++------- tests/store.test.ts | 2 +- 10 files changed, 71 insertions(+), 41 deletions(-) diff --git a/doc/qweb.md b/doc/qweb.md index e20bdf18..3d5a626b 100644 --- a/doc/qweb.md +++ b/doc/qweb.md @@ -294,14 +294,14 @@ There is another way to format a string attribute: the `t-attf-` directive. With it, you get string interpolation: ```xml -
+
``` For historical reason, there is an alternate form of string interpolation: ```xml -
+
``` @@ -480,6 +480,24 @@ some method on a sub widget. Note: if used on a component, the reference will be set in the `refs` variable between `willPatch` and `patched`. +The `t-ref` directive also accepts dynamic values with string interpolation +(like the `t-attf-` directive). For example, if we have `id` set to 44 in the +rendering context, + +```xml +
+``` + +```js +this.refs.widget_44; +``` + +Similarly to `t-attf-`, there is an alternate form of string interpolation: + +```xml +
+``` + ### `t-key` directive Even though Owl tries to be as declarative as possible, some DOM state is still diff --git a/extras/playground/samples.js b/extras/playground/samples.js index f710e290..3351b395 100644 --- a/extras/playground/samples.js +++ b/extras/playground/samples.js @@ -551,7 +551,7 @@ const TODO_APP_STORE_XML = `
- + `; diff --git a/extras/playground/templates.xml b/extras/playground/templates.xml index 40e73716..5b384e6b 100644 --- a/extras/playground/templates.xml +++ b/extras/playground/templates.xml @@ -2,12 +2,12 @@
-
+
@@ -48,7 +48,7 @@
-
+
\ No newline at end of file diff --git a/src/qweb_core.ts b/src/qweb_core.ts index 46b2a48f..409fcdb5 100644 --- a/src/qweb_core.ts +++ b/src/qweb_core.ts @@ -483,9 +483,6 @@ export class QWeb { props.push(`${key}: _${val}`); } } - function formatter(expr) { - return "${" + ctx.formatExpression(expr) + "}"; - } for (let i = 0; i < attributes.length; i++) { let name = attributes[i].name; @@ -544,17 +541,13 @@ export class QWeb { // attribute contains 'non letters' => we want to quote it attName = '"' + attName + '"'; } - const formattedExpr = value! - .replace(/\{\{.*?\}\}/g, s => formatter(s.slice(2, -2))) - .replace(/\#\{.*?\}/g, s => formatter(s.slice(2, -1))); + const formattedExpr = ctx.interpolate(value); const attID = ctx.generateID(); let staticVal = (node).getAttribute(attName); if (staticVal) { - ctx.addLine( - `var _${attID} = '${staticVal} ' + \`${formattedExpr}\`;` - ); + ctx.addLine(`var _${attID} = '${staticVal} ' + ${formattedExpr};`); } else { - ctx.addLine(`var _${attID} = \`${formattedExpr}\`;`); + ctx.addLine(`var _${attID} = ${formattedExpr};`); } attrs.push(`${attName}: _${attID}`); } @@ -758,4 +751,28 @@ export class Context { const result = r.slice(0, -1); return result; } + + /** + * Perform string interpolation on the given string. Note that if the whole + * string is an expression, it simply returns it (formatted). + * For instance: + * 'Hello {{x}}!' -> `Hello ${x}` + * '{{x}}' -> x + */ + interpolate(s: string): string { + let matches = s.match(/\{\{.*?\}\}/g); + if (matches && matches[0].length === s.length) { + return this.formatExpression(s.slice(2, -2)); + } + matches = s.match(/\#\{.*?\}/g); + if (matches && matches[0].length === s.length) { + return this.formatExpression(s.slice(2, -1)); + } + + let formatter = expr => "${" + this.formatExpression(expr) + "}"; + let r = s + .replace(/\{\{.*?\}\}/g, s => formatter(s.slice(2, -2))) + .replace(/\#\{.*?\}/g, s => formatter(s.slice(2, -1))); + return "`" + r + "`"; + } } diff --git a/src/qweb_extensions.ts b/src/qweb_extensions.ts index 6cd817e2..0d03441c 100644 --- a/src/qweb_extensions.ts +++ b/src/qweb_extensions.ts @@ -82,7 +82,7 @@ QWeb.addDirective({ priority: 95, atNodeCreation({ ctx, value, addNodeHook }) { const refKey = `ref${ctx.generateID()}`; - ctx.addLine(`const ${refKey} = ${ctx.formatExpression(value)}`); + ctx.addLine(`const ${refKey} = ${ctx.interpolate(value)};`); addNodeHook("create", `context.refs[${refKey}] = n.elm;`); } }); @@ -391,7 +391,7 @@ QWeb.addDirective({ let refKey: string = ""; if (ref) { refKey = `ref${ctx.generateID()}`; - ctx.addLine(`const ${refKey} = ${ctx.formatExpression(ref)}`); + ctx.addLine(`const ${refKey} = ${ctx.interpolate(ref)};`); refExpr = `context.refs[${refKey}] = w${widgetID};`; } let transitionsInsertCode = ""; diff --git a/tests/__snapshots__/qweb.test.ts.snap b/tests/__snapshots__/qweb.test.ts.snap index 8a562c6b..9ee9966a 100644 --- a/tests/__snapshots__/qweb.test.ts.snap +++ b/tests/__snapshots__/qweb.test.ts.snap @@ -59,7 +59,7 @@ exports[`attributes format expression 1`] = ` "function anonymous(context,extra ) { var h = this.utils.h; - var _1 = \`\${context['value'] + 37}\`; + var _1 = context['value'] + 37; let c2 = [], p2 = {key:2,attrs:{foo: _1}}; var vn2 = h('div', p2, c2); return vn2; @@ -70,7 +70,7 @@ exports[`attributes format expression, other format 1`] = ` "function anonymous(context,extra ) { var h = this.utils.h; - var _1 = \`\${context['value'] + 37}\`; + var _1 = context['value'] + 37; let c2 = [], p2 = {key:2,attrs:{foo: _1}}; var vn2 = h('div', p2, c2); return vn2; @@ -1494,7 +1494,7 @@ exports[`t-ref can get a dynamic ref on a node 1`] = ` let c2 = [], p2 = {key:2}; var vn2 = h('span', p2, c2); c1.push(vn2); - const ref3 = 'myspan' + 3 + const ref3 = \`myspan\${context['id']}\`; p2.hook = { create: (_, n) => { context.refs[ref3] = n.elm; @@ -1513,7 +1513,7 @@ exports[`t-ref can get a ref on a node 1`] = ` let c2 = [], p2 = {key:2}; var vn2 = h('span', p2, c2); c1.push(vn2); - const ref3 = 'myspan' + const ref3 = \`myspan\`; p2.hook = { create: (_, n) => { context.refs[ref3] = n.elm; @@ -1546,7 +1546,7 @@ exports[`t-ref refs in a loop 1`] = ` let c5 = [], p5 = {key:context['item']}; var vn5 = h('div', p5, c5); c1.push(vn5); - const ref6 = context['item'] + const ref6 = context['item']; p5.hook = { create: (_, n) => { context.refs[ref6] = n.elm; diff --git a/tests/animations.test.ts b/tests/animations.test.ts index 2ff55f07..c72d7eef 100644 --- a/tests/animations.test.ts +++ b/tests/animations.test.ts @@ -145,7 +145,7 @@ describe("animations", () => { env.qweb.addTemplate( "TestWidget", - `
blue
` + `
blue
` ); class TestWidget extends Widget { state = { hide: false }; diff --git a/tests/component.test.ts b/tests/component.test.ts index 9c429c69..12d0b46c 100644 --- a/tests/component.test.ts +++ b/tests/component.test.ts @@ -896,7 +896,7 @@ describe("composition", () => { test("t-refs on widget are widgets", async () => { env.qweb.addTemplate( "WidgetC", - `
Hello
` + `
Hello
` ); class WidgetC extends Widget { widgets = { b: WidgetB }; @@ -912,7 +912,7 @@ describe("composition", () => { "ParentWidget", `
- +
` ); class ParentWidget extends Widget { @@ -938,8 +938,8 @@ describe("composition", () => { "ParentWidget", `
- - + +
` ); class ParentWidget extends Widget { @@ -1004,7 +1004,7 @@ describe("composition", () => { "ParentWidget", `
- +
` ); @@ -2277,7 +2277,7 @@ describe("t-mounted directive", () => { test("combined with a t-ref", async () => { env.qweb.addTemplate( "TestWidget", - `
` + `
` ); class TestWidget extends Widget { f() {} diff --git a/tests/qweb.test.ts b/tests/qweb.test.ts index 4ba216ea..8ea2217c 100644 --- a/tests/qweb.test.ts +++ b/tests/qweb.test.ts @@ -1,10 +1,5 @@ import { QWeb } from "../src/qweb_core"; -import { - normalize, - renderToDOM, - renderToString, - trim -} from "./helpers"; +import { normalize, renderToDOM, renderToString, trim } from "./helpers"; //------------------------------------------------------------------------------ // Setup and helpers @@ -1056,16 +1051,16 @@ describe("t-on", () => { describe("t-ref", () => { test("can get a ref on a node", () => { - qweb.addTemplate("test", `
`); + qweb.addTemplate("test", `
`); let refs: any = {}; renderToDOM(qweb, "test", { refs }); expect(refs.myspan.tagName).toBe("SPAN"); }); test("can get a dynamic ref on a node", () => { - qweb.addTemplate("test", `
`); + qweb.addTemplate("test", `
`); let refs: any = {}; - renderToDOM(qweb, "test", { refs }); + renderToDOM(qweb, "test", { refs, id: 3 }); expect(refs.myspan3.tagName).toBe("SPAN"); }); @@ -1075,7 +1070,7 @@ describe("t-ref", () => { `
-
+
` ); diff --git a/tests/store.test.ts b/tests/store.test.ts index 601f3b47..4f361703 100644 --- a/tests/store.test.ts +++ b/tests/store.test.ts @@ -876,7 +876,7 @@ describe("connecting a component to store", () => { "Parent", `
- +
` );