mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] compiler: add support for #{...} in string interpolation
This commit is contained in:
committed by
Sam Degueldre
parent
a3111eb9ca
commit
385e118e58
@@ -290,10 +290,11 @@ If an expression evaluates to a falsy value, it will not be set at all:
|
|||||||
|
|
||||||
It is sometimes convenient to format an attribute with string interpolation. In
|
It is sometimes convenient to format an attribute with string interpolation. In
|
||||||
that case, the `t-attf-` directive can be used. It is useful when we need to mix
|
that case, the `t-attf-` directive can be used. It is useful when we need to mix
|
||||||
literal and dynamic elements, such as css classes.
|
literal and dynamic elements, such as css classes. The dynamic elements can be
|
||||||
|
specified with either `{{...}}` or `#{...}`:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<div t-attf-foo="a {{value1}} is {{value2}} of {{value3}} ]"/>
|
<div t-attf-foo="a {{value1}} is #{value2} of {{value3}} ]"/>
|
||||||
<!-- result if values are set to 1,2 and 3: <div foo="a 0 is 1 of 2 ]"></div> -->
|
<!-- result if values are set to 1,2 and 3: <div foo="a 0 is 1 of 2 ]"></div> -->
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import { compileExpr, compileExprToArray, interpolate, INTERP_REGEXP } from "./inline_expressions";
|
import {
|
||||||
|
compileExpr,
|
||||||
|
compileExprToArray,
|
||||||
|
interpolate,
|
||||||
|
INTERP_REGEXP,
|
||||||
|
replaceDynamicParts,
|
||||||
|
} from "./inline_expressions";
|
||||||
import {
|
import {
|
||||||
AST,
|
AST,
|
||||||
ASTComment,
|
ASTComment,
|
||||||
@@ -606,11 +612,8 @@ export class CodeGenerator {
|
|||||||
this.target.hasRef = true;
|
this.target.hasRef = true;
|
||||||
const isDynamic = INTERP_REGEXP.test(ast.ref);
|
const isDynamic = INTERP_REGEXP.test(ast.ref);
|
||||||
if (isDynamic) {
|
if (isDynamic) {
|
||||||
const str = ast.ref.replace(
|
const str = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true));
|
||||||
INTERP_REGEXP,
|
const idx = block!.insertData(`(el) => refs[${str}] = el`, "ref");
|
||||||
(expr) => "${" + this.captureExpression(expr.slice(2, -2), true) + "}"
|
|
||||||
);
|
|
||||||
const idx = block!.insertData(`(el) => refs[\`${str}\`] = el`, "ref");
|
|
||||||
attrs["block-ref"] = String(idx);
|
attrs["block-ref"] = String(idx);
|
||||||
} else {
|
} else {
|
||||||
let name = ast.ref;
|
let name = ast.ref;
|
||||||
|
|||||||
@@ -355,15 +355,20 @@ export function compileExpr(expr: string): string {
|
|||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
export const INTERP_REGEXP = /\{\{.*?\}\}/g;
|
export const INTERP_REGEXP = /\{\{.*?\}\}|\#\{.*?\}/g;
|
||||||
const INTERP_GROUP_REGEXP = /\{\{.*?\}\}/g;
|
|
||||||
|
|
||||||
export function interpolate(s: string): string {
|
export function replaceDynamicParts(s: string, replacer: (s: string) => string) {
|
||||||
let matches = s.match(INTERP_REGEXP);
|
let matches = s.match(INTERP_REGEXP);
|
||||||
if (matches && matches[0].length === s.length) {
|
if (matches && matches[0].length === s.length) {
|
||||||
return `(${compileExpr(s.slice(2, -2))})`;
|
return `(${replacer(s.slice(2, matches[0][0] === "{" ? -2 : -1))})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = s.replace(INTERP_GROUP_REGEXP, (s) => "${" + compileExpr(s.slice(2, -2)) + "}");
|
let r = s.replace(
|
||||||
|
INTERP_REGEXP,
|
||||||
|
(s) => "${" + replacer(s.slice(2, s[0] === "{" ? -2 : -1)) + "}"
|
||||||
|
);
|
||||||
return "`" + r + "`";
|
return "`" + r + "`";
|
||||||
}
|
}
|
||||||
|
export function interpolate(s: string): string {
|
||||||
|
return replaceDynamicParts(s, compileExpr);
|
||||||
|
}
|
||||||
|
|||||||
@@ -387,6 +387,20 @@ exports[`attributes static attributes with dashes 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`attributes string interpolation, alternate syntax 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let attr1 = \`b\${ctx['value']}r\`;
|
||||||
|
return block1([attr1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`attributes t-att-class and class should combine together 1`] = `
|
exports[`attributes t-att-class and class should combine together 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -16,6 +16,22 @@ exports[`t-ref can get a dynamic ref on a node 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-ref can get a dynamic ref on a node, alternate syntax 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const refs = ctx.__owl__.refs;
|
||||||
|
const v1 = ctx['id'];
|
||||||
|
let ref1 = (el) => refs[\`myspan\${v1}\`] = el;
|
||||||
|
return block1([ref1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`t-ref can get a ref on a node 1`] = `
|
exports[`t-ref can get a ref on a node 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -99,7 +115,7 @@ exports[`t-ref refs in a loop 1`] = `
|
|||||||
const key1 = ctx['item'];
|
const key1 = ctx['item'];
|
||||||
const tKey_1 = ctx['item'];
|
const tKey_1 = ctx['item'];
|
||||||
const v1 = ctx['item'];
|
const v1 = ctx['item'];
|
||||||
let ref1 = (el) => refs[\`\${v1}\`] = el;
|
let ref1 = (el) => refs[(v1)] = el;
|
||||||
let txt1 = ctx['item'];
|
let txt1 = ctx['item'];
|
||||||
c_block2[i1] = withKey(block3([ref1, txt1]), tKey_1 + key1);
|
c_block2[i1] = withKey(block3([ref1, txt1]), tKey_1 + key1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,12 @@ describe("attributes", () => {
|
|||||||
expect(result).toBe(`<div foo="bar"></div>`);
|
expect(result).toBe(`<div foo="bar"></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("string interpolation, alternate syntax", () => {
|
||||||
|
const template = `<div t-attf-foo="b#{value}r"/>`;
|
||||||
|
const result = renderToString(template, { value: "a" });
|
||||||
|
expect(result).toBe(`<div foo="bar"></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
test("t-attf-class", () => {
|
test("t-attf-class", () => {
|
||||||
const template = `<div t-attf-class="hello"/>`;
|
const template = `<div t-attf-class="hello"/>`;
|
||||||
const result = renderToString(template);
|
const result = renderToString(template);
|
||||||
|
|||||||
@@ -27,6 +27,15 @@ describe("t-ref", () => {
|
|||||||
expect(refs.myspan3.tagName).toBe("SPAN");
|
expect(refs.myspan3.tagName).toBe("SPAN");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("can get a dynamic ref on a node, alternate syntax", () => {
|
||||||
|
const template = `<div><span t-ref="myspan#{id}"/></div>`;
|
||||||
|
const refs: any = {};
|
||||||
|
const bdom = renderToBdom(template, { id: 3, __owl__: { refs } });
|
||||||
|
expect(refs).toEqual({});
|
||||||
|
mount(bdom, document.createElement("div"));
|
||||||
|
expect(refs.myspan3.tagName).toBe("SPAN");
|
||||||
|
});
|
||||||
|
|
||||||
test("refs in a loop", () => {
|
test("refs in a loop", () => {
|
||||||
const template = `<div>
|
const template = `<div>
|
||||||
<t t-foreach="items" t-as="item" t-key="item">
|
<t t-foreach="items" t-as="item" t-key="item">
|
||||||
|
|||||||
Reference in New Issue
Block a user