mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] compiler: add support for t-for directive
This commit adds support for the `t-for` whose syntax and usage is similar to the syntax of the for..of loop in JS (and in fact it compiles to a for..of loop). This looping construct supports looping on arbitrary iterables and destructuring assignments, which would be difficult to support in a backward compatible manner on the existing t-foreach directive.
This commit is contained in:
@@ -117,7 +117,7 @@ const { loadFile, mount } = owl;
|
|||||||
Dev mode activates some additional checks and developer amenities:
|
Dev mode activates some additional checks and developer amenities:
|
||||||
|
|
||||||
- [Props validation](./props.md#props-validation) is performed
|
- [Props validation](./props.md#props-validation) is performed
|
||||||
- [t-foreach](./templates.md#loops) loops check for key unicity
|
- [t-for and t-foreach](./templates.md#loops) loops check for key unicity
|
||||||
- Lifecycle hooks are wrapped to report their errors in a more developer-friendly way
|
- Lifecycle hooks are wrapped to report their errors in a more developer-friendly way
|
||||||
- onWillStart and onWillUpdateProps will emit a warning in the console when they
|
- onWillStart and onWillUpdateProps will emit a warning in the console when they
|
||||||
take longer than 3 seconds in an effort to ease debugging the presence of deadlocks
|
take longer than 3 seconds in an effort to ease debugging the presence of deadlocks
|
||||||
|
|||||||
+31
-20
@@ -61,7 +61,7 @@ For reference, here is a list of all standard QWeb directives:
|
|||||||
| `t-out` | [Outputting value, possibly without escaping](#outputting-data) |
|
| `t-out` | [Outputting value, possibly without escaping](#outputting-data) |
|
||||||
| `t-set`, `t-value` | [Setting variables](#setting-variables) |
|
| `t-set`, `t-value` | [Setting variables](#setting-variables) |
|
||||||
| `t-if`, `t-elif`, `t-else`, | [conditionally rendering](#conditionals) |
|
| `t-if`, `t-elif`, `t-else`, | [conditionally rendering](#conditionals) |
|
||||||
| `t-foreach`, `t-as` | [Loops](#loops) |
|
| `t-for/t-of`, `t-foreach/t-as` | [Loops](#loops) |
|
||||||
| `t-att`, `t-attf-*`, `t-att-*` | [Dynamic attributes](#dynamic-attributes) |
|
| `t-att`, `t-attf-*`, `t-att-*` | [Dynamic attributes](#dynamic-attributes) |
|
||||||
| `t-call` | [Rendering sub templates](#sub-templates) |
|
| `t-call` | [Rendering sub templates](#sub-templates) |
|
||||||
| `t-debug`, `t-log` | [Debugging](#debugging) |
|
| `t-debug`, `t-log` | [Debugging](#debugging) |
|
||||||
@@ -372,6 +372,24 @@ Like conditions, `t-foreach` applies to the element bearing the directive’s at
|
|||||||
|
|
||||||
is equivalent to the previous example.
|
is equivalent to the previous example.
|
||||||
|
|
||||||
|
Owl also has another pair of directives that can be used for looping that allow
|
||||||
|
destructuring the contents of the loop item: `t-for` and `t-of`, which behaves
|
||||||
|
much like `for..of` in javascript:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<t t-for="[left, right]" t-of="[['a', 1], ['b', 2], ['c', 3]]" t-key="left">
|
||||||
|
<p><t t-esc="left"/>: <t t-esc="right"/></p>
|
||||||
|
</t>
|
||||||
|
```
|
||||||
|
|
||||||
|
will be rendered as:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<p>a: 1</p>
|
||||||
|
<p>b: 2</p>
|
||||||
|
<p>c: 3</p>
|
||||||
|
```
|
||||||
|
|
||||||
An important difference should be made with the usual `QWeb` behaviour: Owl
|
An important difference should be made with the usual `QWeb` behaviour: Owl
|
||||||
requires the presence of a `t-key` directive, to be able to properly reconcile
|
requires the presence of a `t-key` directive, to be able to properly reconcile
|
||||||
renderings.
|
renderings.
|
||||||
@@ -380,8 +398,9 @@ renderings.
|
|||||||
and maps, it will expose the key of the current iteration as the contents of the
|
and maps, it will expose the key of the current iteration as the contents of the
|
||||||
`t-as`, and the corresponding value with the same name and the suffix `_value`.
|
`t-as`, and the corresponding value with the same name and the suffix `_value`.
|
||||||
|
|
||||||
In addition to the name passed via t-as, `t-foreach` provides a few other useful
|
In addition to the name passed via t-as, `t-foreach` (but not `t-for`) provides
|
||||||
variables (note: `$as` will be replaced with the name passed to `t-as`):
|
a few other useful variables (note: `$as` will be replaced with the name passed
|
||||||
|
to `t-as`):
|
||||||
|
|
||||||
- `$as_value`: the current iteration value, identical to `$as` for arrays and
|
- `$as_value`: the current iteration value, identical to `$as` for arrays and
|
||||||
other iterables, but for objects and maps, it provides the value (where `$as`
|
other iterables, but for objects and maps, it provides the value (where `$as`
|
||||||
@@ -393,10 +412,9 @@ variables (note: `$as` will be replaced with the name passed to `t-as`):
|
|||||||
(equivalent to `$as_index + 1 == $as_size`), requires the iteratee’s size be
|
(equivalent to `$as_index + 1 == $as_size`), requires the iteratee’s size be
|
||||||
available
|
available
|
||||||
|
|
||||||
These extra variables provided and all new variables created into the `t-foreach`
|
These variables and all new variables created inside`t-foreach` and `t-for` are
|
||||||
are only available in the scope of the `t-foreach`. If the variable exists outside
|
only available inside of the loop. If a variable existed outside the context of
|
||||||
the context of the `t-foreach`, the value is copied at the end of the foreach
|
the loop, the assignment will affect the outer variable.
|
||||||
into the global context.
|
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<t t-set="existing_variable" t-value="false"/>
|
<t t-set="existing_variable" t-value="false"/>
|
||||||
@@ -408,7 +426,7 @@ into the global context.
|
|||||||
<!-- existing_variable and new_variable now true -->
|
<!-- existing_variable and new_variable now true -->
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- existing_variable always true -->
|
<!-- existing_variable still true -->
|
||||||
<!-- new_variable undefined -->
|
<!-- new_variable undefined -->
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -471,18 +489,11 @@ are all equivalent:
|
|||||||
</t>
|
</t>
|
||||||
```
|
```
|
||||||
|
|
||||||
If there is no `t-key` directive, Owl will use the index as a default key.
|
The `t-key` directive is mandatory, and as mentioned should represent the object's
|
||||||
|
identity. You may be tempted to use the loop index as a key, but keep in mind that
|
||||||
Note: the `t-foreach` directive only accepts arrays (lists) or objects. It does
|
this is only correct if items in the loop cannot be reordered. If this is not the
|
||||||
not work with other iterables, such as `Set`. However, it is only a matter of
|
case, using the index as the key can lead to bugs that are difficult to find, so
|
||||||
using the `...` javascript operator. For example:
|
use the index as the key only if you are sure items cannot be reordered.
|
||||||
|
|
||||||
```xml
|
|
||||||
<t t-foreach="[...items]" t-as="item">...</t>
|
|
||||||
```
|
|
||||||
|
|
||||||
The `...` operator will convert the `Set` (or any other iterables) into a list,
|
|
||||||
which will work with Owl QWeb.
|
|
||||||
|
|
||||||
### Sub Templates
|
### Sub Templates
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
ASTTCallBlock,
|
ASTTCallBlock,
|
||||||
ASTTEsc,
|
ASTTEsc,
|
||||||
ASTText,
|
ASTText,
|
||||||
|
ASTTFor,
|
||||||
ASTTForEach,
|
ASTTForEach,
|
||||||
ASTTif,
|
ASTTif,
|
||||||
ASTTKey,
|
ASTTKey,
|
||||||
@@ -471,6 +472,8 @@ export class CodeGenerator {
|
|||||||
return this.compileTIf(ast, ctx);
|
return this.compileTIf(ast, ctx);
|
||||||
case ASTType.TForEach:
|
case ASTType.TForEach:
|
||||||
return this.compileTForeach(ast, ctx);
|
return this.compileTForeach(ast, ctx);
|
||||||
|
case ASTType.TFor:
|
||||||
|
return this.compileTFor(ast, ctx);
|
||||||
case ASTType.TKey:
|
case ASTType.TKey:
|
||||||
return this.compileTKey(ast, ctx);
|
return this.compileTKey(ast, ctx);
|
||||||
case ASTType.Multi:
|
case ASTType.Multi:
|
||||||
@@ -907,7 +910,7 @@ export class CodeGenerator {
|
|||||||
if (!ast.hasNoValue) {
|
if (!ast.hasNoValue) {
|
||||||
this.addLine(`ctx[\`${ast.elem}_value\`] = ${keys}[${loopVar}];`);
|
this.addLine(`ctx[\`${ast.elem}_value\`] = ${keys}[${loopVar}];`);
|
||||||
}
|
}
|
||||||
this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar);
|
this.define(`key${this.target.loopLevel}`, compileExpr(ast.key));
|
||||||
if (this.dev) {
|
if (this.dev) {
|
||||||
// Throw error on duplicate keys in dev mode
|
// Throw error on duplicate keys in dev mode
|
||||||
this.helpers.add("OwlError");
|
this.helpers.add("OwlError");
|
||||||
@@ -954,6 +957,46 @@ export class CodeGenerator {
|
|||||||
return block.varName;
|
return block.varName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileTFor(ast: ASTTFor, ctx: Context): string {
|
||||||
|
let { block } = ctx;
|
||||||
|
if (block) {
|
||||||
|
this.insertAnchor(block);
|
||||||
|
}
|
||||||
|
block = this.createBlock(block, "list", ctx);
|
||||||
|
this.target.loopLevel++;
|
||||||
|
this.addLine(`ctx = Object.create(ctx);`);
|
||||||
|
// Throw errors on duplicate keys in dev mode
|
||||||
|
if (this.dev) {
|
||||||
|
this.define(`keys${block.id}`, `new Set()`);
|
||||||
|
}
|
||||||
|
this.define(`c_block${block.id}`, "[]");
|
||||||
|
const index = `i${this.target.loopLevel}`;
|
||||||
|
this.addLine(`let ${index} = ${0};`);
|
||||||
|
const binding = compileExpr(ast.binding);
|
||||||
|
this.addLine(`for (${binding} of ${compileExpr(ast.iterable)}) {`);
|
||||||
|
this.target.indentLevel++;
|
||||||
|
this.define(`key${this.target.loopLevel}`, compileExpr(ast.key));
|
||||||
|
if (this.dev) {
|
||||||
|
// Throw error on duplicate keys in dev mode
|
||||||
|
this.helpers.add("OwlError");
|
||||||
|
this.addLine(
|
||||||
|
`if (keys${block.id}.has(String(key${this.target.loopLevel}))) { throw new OwlError(\`Got duplicate key in t-for: \${key${this.target.loopLevel}}\`)}`
|
||||||
|
);
|
||||||
|
this.addLine(`keys${block.id}.add(String(key${this.target.loopLevel}));`);
|
||||||
|
}
|
||||||
|
const subCtx = createContext(ctx, { block, index });
|
||||||
|
this.compileAST(ast.body, subCtx);
|
||||||
|
this.addLine(`${index}++;`);
|
||||||
|
this.target.indentLevel--;
|
||||||
|
this.target.loopLevel--;
|
||||||
|
this.addLine(`}`);
|
||||||
|
if (!ctx.isLast) {
|
||||||
|
this.addLine(`ctx = ctx.__proto__;`);
|
||||||
|
}
|
||||||
|
this.insertBlock("l", block, ctx);
|
||||||
|
return block.varName;
|
||||||
|
}
|
||||||
|
|
||||||
compileTKey(ast: ASTTKey, ctx: Context): string | null {
|
compileTKey(ast: ASTTKey, ctx: Context): string | null {
|
||||||
const tKeyExpr = generateId("tKey_");
|
const tKeyExpr = generateId("tKey_");
|
||||||
this.define(tKeyExpr, compileExpr(ast.expr));
|
this.define(tKeyExpr, compileExpr(ast.expr));
|
||||||
|
|||||||
+42
-1
@@ -26,6 +26,7 @@ export const enum ASTType {
|
|||||||
TCallBlock,
|
TCallBlock,
|
||||||
TTranslation,
|
TTranslation,
|
||||||
TPortal,
|
TPortal,
|
||||||
|
TFor,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTText {
|
export interface ASTText {
|
||||||
@@ -104,7 +105,15 @@ export interface ASTTForEach {
|
|||||||
hasNoLast: boolean;
|
hasNoLast: boolean;
|
||||||
hasNoIndex: boolean;
|
hasNoIndex: boolean;
|
||||||
hasNoValue: boolean;
|
hasNoValue: boolean;
|
||||||
key: string | null;
|
key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ASTTFor {
|
||||||
|
type: ASTType.TFor;
|
||||||
|
iterable: string;
|
||||||
|
binding: string;
|
||||||
|
body: AST;
|
||||||
|
key: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ASTTKey {
|
export interface ASTTKey {
|
||||||
@@ -183,6 +192,7 @@ export type AST =
|
|||||||
| ASTTCall
|
| ASTTCall
|
||||||
| ASTTOut
|
| ASTTOut
|
||||||
| ASTTForEach
|
| ASTTForEach
|
||||||
|
| ASTTFor
|
||||||
| ASTTKey
|
| ASTTKey
|
||||||
| ASTComponent
|
| ASTComponent
|
||||||
| ASTSlot
|
| ASTSlot
|
||||||
@@ -230,6 +240,7 @@ function parseNode(node: Node, ctx: ParsingContext): AST | null {
|
|||||||
return (
|
return (
|
||||||
parseTDebugLog(node, ctx) ||
|
parseTDebugLog(node, ctx) ||
|
||||||
parseTForEach(node, ctx) ||
|
parseTForEach(node, ctx) ||
|
||||||
|
parseTFor(node, ctx) ||
|
||||||
parseTIf(node, ctx) ||
|
parseTIf(node, ctx) ||
|
||||||
parseTPortal(node, ctx) ||
|
parseTPortal(node, ctx) ||
|
||||||
parseTCall(node, ctx) ||
|
parseTCall(node, ctx) ||
|
||||||
@@ -531,6 +542,36 @@ function parseTForEach(node: Element, ctx: ParsingContext): AST | null {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseTFor(node: Element, ctx: ParsingContext): AST | null {
|
||||||
|
if (!node.hasAttribute("t-for")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const binding = node.getAttribute("t-for")!;
|
||||||
|
node.removeAttribute("t-for");
|
||||||
|
const iterable = node.getAttribute("t-of") || "";
|
||||||
|
node.removeAttribute("t-of");
|
||||||
|
const key = node.getAttribute("t-key");
|
||||||
|
if (!key) {
|
||||||
|
throw new OwlError(
|
||||||
|
`"Directive t-for should always be used with a t-key!" (expression: t-for="${binding}" t-of="${iterable}")`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
node.removeAttribute("t-key");
|
||||||
|
const body = parseNode(node, ctx);
|
||||||
|
|
||||||
|
if (!body) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: ASTType.TFor,
|
||||||
|
iterable,
|
||||||
|
binding,
|
||||||
|
body,
|
||||||
|
key,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function parseTKey(node: Element, ctx: ParsingContext): AST | null {
|
function parseTKey(node: Element, ctx: ParsingContext): AST | null {
|
||||||
if (!node.hasAttribute("t-key")) {
|
if (!node.hasAttribute("t-key")) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -0,0 +1,670 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`t-for destructuring array items 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for ([ctx['key'],ctx['value']] of Object.entries({a:1,b:2})) {
|
||||||
|
const key1 = ctx['key'];
|
||||||
|
const b3 = text(\`(\`);
|
||||||
|
const b4 = text(ctx['key']);
|
||||||
|
const b5 = text(\`: \`);
|
||||||
|
const b6 = text(ctx['value']);
|
||||||
|
const b7 = text(\`)\`);
|
||||||
|
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for destructuring array items: rest 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for ([ctx['head'],...ctx['tail']] of [[1,2,3],[4,5,6]]) {
|
||||||
|
const key1 = ctx['head'];
|
||||||
|
const b3 = text(\`(\`);
|
||||||
|
const b4 = text(ctx['head']);
|
||||||
|
const b5 = text(\`;\`);
|
||||||
|
const b6 = text(ctx['tail']);
|
||||||
|
const b7 = text(\`)\`);
|
||||||
|
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for destructuring object items 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for ({k:ctx['k'],v:ctx['v']} of [{k:'a',v:1},{k:'b',v:2}]) {
|
||||||
|
const key1 = ctx['k'];
|
||||||
|
const b3 = text(\`(\`);
|
||||||
|
const b4 = text(ctx['k']);
|
||||||
|
const b5 = text(\`: \`);
|
||||||
|
const b6 = text(ctx['v']);
|
||||||
|
const b7 = text(\`)\`);
|
||||||
|
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for destructuring object items: rest 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for ({k:ctx['k'],v:ctx['v']} of [{k:'a',v:1},{k:'b',v:2}]) {
|
||||||
|
const key1 = ctx['k'];
|
||||||
|
const b3 = text(\`(\`);
|
||||||
|
const b4 = text(ctx['k']);
|
||||||
|
const b5 = text(\`: \`);
|
||||||
|
const b6 = text(ctx['v']);
|
||||||
|
const b7 = text(\`)\`);
|
||||||
|
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for does not pollute the rendering context 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of [1]) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
c_block2[i1] = withKey(text(ctx['item']), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for iterate on items (on a element node) 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block3 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of [1,2]) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
let txt1 = ctx['item'];
|
||||||
|
c_block2[i1] = withKey(block3([txt1]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for iterate, Map param 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for ([ctx['key'],ctx['value']] of ctx['map']) {
|
||||||
|
const key1 = ctx['key'];
|
||||||
|
const b3 = text(\` [\`);
|
||||||
|
const b4 = text(ctx['key']);
|
||||||
|
const b5 = text(\`: \`);
|
||||||
|
const b6 = text(ctx['value']);
|
||||||
|
const b7 = text(\`] \`);
|
||||||
|
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for iterate, Set param 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of ctx['set']) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
c_block1[i1] = withKey(text(ctx['item']), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for iterate, generator param 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of ctx['gen']()) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
c_block1[i1] = withKey(text(ctx['item']), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for iterate, iterable param 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of ctx['map'].values()) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
c_block1[i1] = withKey(text(ctx['item']), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for nested destructuring 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for ([ctx['key'],{left:ctx['left'],right:ctx['right']}] of Object.entries(ctx['obj'])) {
|
||||||
|
const key1 = ctx['key'];
|
||||||
|
const b3 = text(\`(\`);
|
||||||
|
const b4 = text(ctx['key']);
|
||||||
|
const b5 = text(\`: [\`);
|
||||||
|
const b6 = text(ctx['left']);
|
||||||
|
const b7 = text(\`, \`);
|
||||||
|
const b8 = text(ctx['right']);
|
||||||
|
const b9 = text(\`])\`);
|
||||||
|
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7, b8, b9]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for simple iteration (in a node) 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of [3,2,1]) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
c_block2[i1] = withKey(text(ctx['item']), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for simple iteration 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of [3,2,1]) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
c_block1[i1] = withKey(text(ctx['item']), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for simple iteration with two nodes inside 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
let block3 = createBlock(\`<span>a<block-text-0/></span>\`);
|
||||||
|
let block4 = createBlock(\`<span>b<block-text-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of [3,2,1]) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
let txt1 = ctx['item'];
|
||||||
|
const b3 = block3([txt1]);
|
||||||
|
let txt2 = ctx['item'];
|
||||||
|
const b4 = block4([txt2]);
|
||||||
|
c_block1[i1] = withKey(multi([b3, b4]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-call with body in t-for in t-for 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
||||||
|
const callTemplate_1 = app.getTemplate(\`sub\`);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/><span>[<block-text-0/>][<block-text-1/>][<block-text-2/>]</span></div>\`);
|
||||||
|
let block6 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['a'] of ctx['numbers']) {
|
||||||
|
const key1 = ctx['a'];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block4 = [];
|
||||||
|
let i2 = 0;
|
||||||
|
for (ctx['b'] of ctx['letters']) {
|
||||||
|
const key2 = ctx['b'];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1;
|
||||||
|
setContextValue(ctx, \\"c\\", 'x'+'_'+ctx['a']+'_'+ctx['b']);
|
||||||
|
c_block4[i2] = withKey(callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}__\${key2}\`), key2);
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
i2++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
const b4 = list(c_block4);
|
||||||
|
let txt1 = ctx['c'];
|
||||||
|
const b6 = block6([txt1]);
|
||||||
|
c_block2[i1] = withKey(multi([b4, b6]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
let txt2 = ctx['a'];
|
||||||
|
let txt3 = ctx['b'];
|
||||||
|
let txt4 = ctx['c'];
|
||||||
|
return block1([txt2, txt3, txt4], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-call with body in t-for in t-for 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const b2 = text(\` [\`);
|
||||||
|
const b3 = text(ctx['a']);
|
||||||
|
const b4 = text(\`] [\`);
|
||||||
|
const b5 = text(ctx['b']);
|
||||||
|
const b6 = text(\`] [\`);
|
||||||
|
const b7 = text(ctx['c']);
|
||||||
|
const b8 = text(\`] \`);
|
||||||
|
return multi([b2, b3, b4, b5, b6, b7, b8]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-call without body in t-for in t-for 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const callTemplate_1 = app.getTemplate(\`sub\`);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/><span>[<block-text-0/>][<block-text-1/>][<block-text-2/>]</span></div>\`);
|
||||||
|
let block6 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['a'] of ctx['numbers']) {
|
||||||
|
const key1 = ctx['a'];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block4 = [];
|
||||||
|
let i2 = 0;
|
||||||
|
for (ctx['b'] of ctx['letters']) {
|
||||||
|
const key2 = ctx['b'];
|
||||||
|
c_block4[i2] = withKey(callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}__\${key2}\`), key2);
|
||||||
|
i2++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
const b4 = list(c_block4);
|
||||||
|
let txt1 = ctx['c'];
|
||||||
|
const b6 = block6([txt1]);
|
||||||
|
c_block2[i1] = withKey(multi([b4, b6]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
let txt2 = ctx['a'];
|
||||||
|
let txt3 = ctx['b'];
|
||||||
|
let txt4 = ctx['c'];
|
||||||
|
return block1([txt2, txt3, txt4], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-call without body in t-for in t-for 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { isBoundary, withDefault, setContextValue } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1
|
||||||
|
setContextValue(ctx, \\"c\\", 'x'+'_'+ctx['a']+'_'+ctx['b']);
|
||||||
|
const b2 = text(\` [\`);
|
||||||
|
const b3 = text(ctx['a']);
|
||||||
|
const b4 = text(\`] [\`);
|
||||||
|
const b5 = text(ctx['b']);
|
||||||
|
const b6 = text(\`] [\`);
|
||||||
|
const b7 = text(ctx['c']);
|
||||||
|
const b8 = text(\`] \`);
|
||||||
|
return multi([b2, b3, b4, b5, b6, b7, b8]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-for in t-for 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['number'] of ctx['numbers']) {
|
||||||
|
const key1 = ctx['number'];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block3 = [];
|
||||||
|
let i2 = 0;
|
||||||
|
for (ctx['letter'] of ctx['letters']) {
|
||||||
|
const key2 = ctx['letter'];
|
||||||
|
const b5 = text(\` [\`);
|
||||||
|
const b6 = text(ctx['number']);
|
||||||
|
const b7 = text(ctx['letter']);
|
||||||
|
const b8 = text(\`] \`);
|
||||||
|
c_block3[i2] = withKey(multi([b5, b6, b7, b8]), key2);
|
||||||
|
i2++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
c_block2[i1] = withKey(list(c_block3), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-for in t-foreach 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { prepareList, withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['numbers']);;
|
||||||
|
for (let i1 = 0; i1 < l_block2; i1++) {
|
||||||
|
ctx[\`number\`] = v_block2[i1];
|
||||||
|
const key1 = ctx['number'];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block3 = [];
|
||||||
|
let i2 = 0;
|
||||||
|
for (ctx['letter'] of ctx['letters']) {
|
||||||
|
const key2 = ctx['letter'];
|
||||||
|
const b5 = text(\` [\`);
|
||||||
|
const b6 = text(ctx['number']);
|
||||||
|
const b7 = text(ctx['letter']);
|
||||||
|
const b8 = text(\`] \`);
|
||||||
|
c_block3[i2] = withKey(multi([b5, b6, b7, b8]), key2);
|
||||||
|
i2++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
c_block2[i1] = withKey(list(c_block3), key1);
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-for with t-if inside (no external node) 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
let block3 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for ({id:ctx['id'],text:ctx['text']} of ctx['elems']) {
|
||||||
|
const key1 = ctx['id'];
|
||||||
|
let b3;
|
||||||
|
if (ctx['id']<3) {
|
||||||
|
let txt1 = ctx['text'];
|
||||||
|
b3 = block3([txt1]);
|
||||||
|
}
|
||||||
|
c_block1[i1] = withKey(multi([b3]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-for with t-if inside 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block4 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for ({id:ctx['id'],text:ctx['text']} of ctx['elems']) {
|
||||||
|
const key1 = ctx['id'];
|
||||||
|
let b4;
|
||||||
|
if (ctx['id']<3) {
|
||||||
|
let txt1 = ctx['text'];
|
||||||
|
b4 = block4([txt1]);
|
||||||
|
}
|
||||||
|
c_block2[i1] = withKey(multi([b4]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-foreach in t-for 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { prepareList, withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['number'] of ctx['numbers']) {
|
||||||
|
const key1 = ctx['number'];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const [k_block3, v_block3, l_block3, c_block3] = prepareList(ctx['letters']);;
|
||||||
|
for (let i2 = 0; i2 < l_block3; i2++) {
|
||||||
|
ctx[\`letter\`] = v_block3[i2];
|
||||||
|
const key2 = ctx['letter'];
|
||||||
|
const b5 = text(\` [\`);
|
||||||
|
const b6 = text(ctx['number']);
|
||||||
|
const b7 = text(ctx['letter']);
|
||||||
|
const b8 = text(\`] \`);
|
||||||
|
c_block3[i2] = withKey(multi([b5, b6, b7, b8]), key2);
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
c_block2[i1] = withKey(list(c_block3), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for t-key on t-for 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block3 = createBlock(\`<span/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['thing'] of ctx['things']) {
|
||||||
|
const key1 = ctx['thing'];
|
||||||
|
c_block2[i1] = withKey(block3(), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-for throws error if invalid loop expression 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block3 = createBlock(\`<span/>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of ctx['abc']) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
const tKey_1 = ctx['item'];
|
||||||
|
c_block2[i1] = withKey(block3(), tKey_1 + key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
@@ -335,6 +335,99 @@ exports[`t-set t-set evaluates an expression only once 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-set t-set outside modified in t-for 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/><p>EndLoop: <block-text-0/></p></div>\`);
|
||||||
|
let block3 = createBlock(\`<p>InLoop: <block-text-0/></p>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1
|
||||||
|
setContextValue(ctx, \\"iter\\", 0);
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['val'] of ['a','b']) {
|
||||||
|
const key1 = ctx['val'];
|
||||||
|
let txt1 = ctx['iter'];
|
||||||
|
c_block2[i1] = withKey(block3([txt1]), key1);
|
||||||
|
setContextValue(ctx, \\"iter\\", ctx['iter']+1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
let txt2 = ctx['iter'];
|
||||||
|
return block1([txt2], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-set t-set outside modified in t-for increment-after operator 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/><p>EndLoop: <block-text-0/></p></div>\`);
|
||||||
|
let block3 = createBlock(\`<p>InLoop: <block-text-0/></p>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1
|
||||||
|
setContextValue(ctx, \\"iter\\", 0);
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['val'] of ['a','b']) {
|
||||||
|
const key1 = ctx['val'];
|
||||||
|
let txt1 = ctx['iter'];
|
||||||
|
c_block2[i1] = withKey(block3([txt1]), key1);
|
||||||
|
setContextValue(ctx, \\"iter\\", ctx['iter']++);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
let txt2 = ctx['iter'];
|
||||||
|
return block1([txt2], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`t-set t-set outside modified in t-for increment-before operator 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/><p>EndLoop: <block-text-0/></p></div>\`);
|
||||||
|
let block3 = createBlock(\`<p>InLoop: <block-text-0/></p>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1
|
||||||
|
setContextValue(ctx, \\"iter\\", 0);
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['val'] of ['a','b']) {
|
||||||
|
const key1 = ctx['val'];
|
||||||
|
let txt1 = ctx['iter'];
|
||||||
|
c_block2[i1] = withKey(block3([txt1]), key1);
|
||||||
|
setContextValue(ctx, \\"iter\\", ++ctx['iter']);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
let txt2 = ctx['iter'];
|
||||||
|
return block1([txt2], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`t-set t-set outside modified in t-foreach 1`] = `
|
exports[`t-set t-set outside modified in t-foreach 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
@@ -454,6 +547,35 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`t-set t-set should reuse variable if possible: for..of 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { isBoundary, withDefault, setContextValue, withKey } = helpers;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block3 = createBlock(\`<div><span>v<block-text-0/></span></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
ctx[isBoundary] = 1
|
||||||
|
setContextValue(ctx, \\"v\\", 1);
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['elem'] of ctx['list']) {
|
||||||
|
const key1 = ctx['elem_index'];
|
||||||
|
let txt1 = ctx['v'];
|
||||||
|
setContextValue(ctx, \\"v\\", ctx['elem']);
|
||||||
|
c_block2[i1] = withKey(block3([txt1]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`t-set t-set with content and sub t-esc 1`] = `
|
exports[`t-set t-set with content and sub t-esc 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -0,0 +1,317 @@
|
|||||||
|
import {
|
||||||
|
renderToBdom,
|
||||||
|
renderToString,
|
||||||
|
snapshotEverything,
|
||||||
|
TestContext,
|
||||||
|
makeTestFixture,
|
||||||
|
} from "../helpers";
|
||||||
|
import { mount, patch } from "../../src/runtime/blockdom";
|
||||||
|
|
||||||
|
snapshotEverything();
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// t-for
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
describe("t-for", () => {
|
||||||
|
test("simple iteration", () => {
|
||||||
|
const template = `<t t-for="item" t-of="[3, 2, 1]" t-key="item"><t t-esc="item"/></t>`;
|
||||||
|
expect(renderToString(template)).toBe("321");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("simple iteration with two nodes inside", () => {
|
||||||
|
const template = `
|
||||||
|
<t t-for="item" t-of="[3, 2, 1]" t-key="item">
|
||||||
|
<span>a<t t-esc="item"/></span>
|
||||||
|
<span>b<t t-esc="item"/></span>
|
||||||
|
</t>`;
|
||||||
|
const expected =
|
||||||
|
"<span>a3</span><span>b3</span><span>a2</span><span>b2</span><span>a1</span><span>b1</span>";
|
||||||
|
expect(renderToString(template)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("destructuring array items", () => {
|
||||||
|
const template = `<t t-for="[key, value]" t-of="Object.entries({ a: 1, b: 2 })" t-key="key">(<t t-esc="key"/>: <t t-esc="value"/>)</t>`;
|
||||||
|
expect(renderToString(template)).toBe("(a: 1)(b: 2)");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("destructuring array items: rest", () => {
|
||||||
|
const template = `<t t-for="[head, ...tail]" t-of="[[1, 2, 3], [4, 5, 6]]" t-key="head">(<t t-esc="head"/>;<t t-esc="tail"/>)</t>`;
|
||||||
|
expect(renderToString(template)).toBe("(1;2,3)(4;5,6)");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("destructuring object items", () => {
|
||||||
|
const template = `<t t-for="{ k, v }" t-of="[{ k: 'a', v: 1 }, { k: 'b', v: 2 }]" t-key="k">(<t t-esc="k"/>: <t t-esc="v"/>)</t>`;
|
||||||
|
expect(renderToString(template)).toBe("(a: 1)(b: 2)");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("destructuring object items: rest", () => {
|
||||||
|
const template = `<t t-for="{ k, v }" t-of="[{ k: 'a', v: 1 }, { k: 'b', v: 2 }]" t-key="k">(<t t-esc="k"/>: <t t-esc="v"/>)</t>`;
|
||||||
|
expect(renderToString(template)).toBe("(a: 1)(b: 2)");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("nested destructuring", () => {
|
||||||
|
const template = `<t t-for="[key, {left, right}]" t-of="Object.entries(obj)" t-key="key">(<t t-esc="key"/>: [<t t-esc="left"/>, <t t-esc="right"/>])</t>`;
|
||||||
|
expect(
|
||||||
|
renderToString(template, {
|
||||||
|
obj: {
|
||||||
|
a: { left: 1, right: 2 },
|
||||||
|
b: { left: 3, right: 4 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).toBe("(a: [1, 2])(b: [3, 4])");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-key on t-for", async () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-for="thing" t-of="things" t-key="thing">
|
||||||
|
<span/>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const fixture = makeTestFixture();
|
||||||
|
|
||||||
|
const vnode1 = renderToBdom(template, { things: [1, 2] });
|
||||||
|
mount(vnode1, fixture);
|
||||||
|
let elm = fixture;
|
||||||
|
expect(elm.innerHTML).toBe("<div><span></span><span></span></div>");
|
||||||
|
const first = elm.querySelectorAll("span")[0];
|
||||||
|
const second = elm.querySelectorAll("span")[1];
|
||||||
|
|
||||||
|
const vnode2 = renderToBdom(template, { things: [2, 1] });
|
||||||
|
patch(vnode1, vnode2);
|
||||||
|
|
||||||
|
expect(elm.innerHTML).toBe("<div><span></span><span></span></div>");
|
||||||
|
expect(first).toBe(elm.querySelectorAll("span")[1]);
|
||||||
|
expect(second).toBe(elm.querySelectorAll("span")[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("simple iteration (in a node)", () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-for="item" t-of="[3, 2, 1]" t-key="item"><t t-esc="item"/></t>
|
||||||
|
</div>`;
|
||||||
|
expect(renderToString(template)).toBe("<div>321</div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("iterate on items (on a element node)", () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<span t-for="item" t-of="[1, 2]" t-key="item"><t t-esc="item"/></span>
|
||||||
|
</div>`;
|
||||||
|
const expected = `<div><span>1</span><span>2</span></div>`;
|
||||||
|
expect(renderToString(template)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("iterate, Map param", () => {
|
||||||
|
const template = `
|
||||||
|
<t t-for="[key, value]" t-of="map" t-key="key">
|
||||||
|
[<t t-esc="key"/>: <t t-esc="value"/>]
|
||||||
|
</t>`;
|
||||||
|
const expected = ` [a: 1] [b: 2] [c: 3] `;
|
||||||
|
const context = {
|
||||||
|
map: new Map([
|
||||||
|
["a", 1],
|
||||||
|
["b", 2],
|
||||||
|
["c", 3],
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
expect(renderToString(template, context)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("iterate, Set param", () => {
|
||||||
|
const template = `
|
||||||
|
<t t-for="item" t-of="set" t-key="item">
|
||||||
|
<t t-esc="item"/>
|
||||||
|
</t>`;
|
||||||
|
const expected = `123`;
|
||||||
|
const context = { set: new Set([1, 2, 3]) };
|
||||||
|
expect(renderToString(template, context)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("iterate, iterable param", () => {
|
||||||
|
const template = `
|
||||||
|
<t t-for="item" t-of="map.values()" t-key="item">
|
||||||
|
<t t-esc="item"/>
|
||||||
|
</t>`;
|
||||||
|
const expected = `123`;
|
||||||
|
const context = {
|
||||||
|
map: new Map([
|
||||||
|
["a", 1],
|
||||||
|
["b", 2],
|
||||||
|
["c", 3],
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
expect(renderToString(template, context)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("iterate, generator param", () => {
|
||||||
|
const template = `
|
||||||
|
<t t-for="item" t-of="gen()" t-key="item">
|
||||||
|
<t t-esc="item"/>
|
||||||
|
</t>`;
|
||||||
|
const expected = `123`;
|
||||||
|
const context = {
|
||||||
|
*gen() {
|
||||||
|
yield 1;
|
||||||
|
yield 2;
|
||||||
|
yield 3;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(renderToString(template, context)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not pollute the rendering context", () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-for="item" t-of="[1]" t-key="item"><t t-esc="item"/></t>
|
||||||
|
</div>`;
|
||||||
|
const context = { __owl__: {} };
|
||||||
|
renderToString(template, context);
|
||||||
|
expect(Object.keys(context)).toEqual(["__owl__"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-for in t-for", () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-for="number" t-of="numbers" t-key="number">
|
||||||
|
<t t-for="letter" t-of="letters" t-key="letter">
|
||||||
|
[<t t-esc="number"/><t t-esc="letter"/>]
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const context = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
||||||
|
const expected = "<div> [1a] [1b] [2a] [2b] [3a] [3b] </div>";
|
||||||
|
expect(renderToString(template, context)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-for in t-foreach", () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-foreach="numbers" t-as="number" t-key="number">
|
||||||
|
<t t-for="letter" t-of="letters" t-key="letter">
|
||||||
|
[<t t-esc="number"/><t t-esc="letter"/>]
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const context = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
||||||
|
const expected = "<div> [1a] [1b] [2a] [2b] [3a] [3b] </div>";
|
||||||
|
expect(renderToString(template, context)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-foreach in t-for", () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-for="number" t-of="numbers" t-key="number">
|
||||||
|
<t t-foreach="letters" t-as="letter" t-key="letter">
|
||||||
|
[<t t-esc="number"/><t t-esc="letter"/>]
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
const context = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
||||||
|
const expected = "<div> [1a] [1b] [2a] [2b] [3a] [3b] </div>";
|
||||||
|
expect(renderToString(template, context)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-call without body in t-for in t-for", () => {
|
||||||
|
const context = new TestContext();
|
||||||
|
const sub = `
|
||||||
|
<t>
|
||||||
|
<t t-set="c" t-value="'x' + '_' + a + '_'+ b" />
|
||||||
|
[<t t-esc="a" />]
|
||||||
|
[<t t-esc="b" />]
|
||||||
|
[<t t-esc="c" />]
|
||||||
|
</t>`;
|
||||||
|
|
||||||
|
const main = `
|
||||||
|
<div>
|
||||||
|
<t t-for="a" t-of="numbers" t-key="a">
|
||||||
|
<t t-for="b" t-of="letters" t-key="b">
|
||||||
|
<t t-call="sub" />
|
||||||
|
</t>
|
||||||
|
<span t-esc="c"/>
|
||||||
|
</t>
|
||||||
|
<span>[<t t-esc="a" />][<t t-esc="b" />][<t t-esc="c" />]</span>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
context.addTemplate("sub", sub);
|
||||||
|
context.addTemplate("main", main);
|
||||||
|
|
||||||
|
const ctx = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
||||||
|
const expected =
|
||||||
|
"<div> [1] [a] [x_1_a] [1] [b] [x_1_b] <span></span> [2] [a] [x_2_a] [2] [b] [x_2_b] <span></span> [3] [a] [x_3_a] [3] [b] [x_3_b] <span></span><span>[][][]</span></div>";
|
||||||
|
expect(context.renderToString("main", ctx)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-call with body in t-for in t-for", () => {
|
||||||
|
const context = new TestContext();
|
||||||
|
const sub = `
|
||||||
|
<t>
|
||||||
|
[<t t-esc="a" />]
|
||||||
|
[<t t-esc="b" />]
|
||||||
|
[<t t-esc="c" />]
|
||||||
|
</t>`;
|
||||||
|
|
||||||
|
const main = `
|
||||||
|
<div>
|
||||||
|
<t t-for="a" t-of="numbers" t-key="a">
|
||||||
|
<t t-for="b" t-of="letters" t-key="b">
|
||||||
|
<t t-call="sub" >
|
||||||
|
<t t-set="c" t-value="'x' + '_' + a + '_'+ b" />
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
<span t-esc="c"/>
|
||||||
|
</t>
|
||||||
|
<span>[<t t-esc="a" />][<t t-esc="b" />][<t t-esc="c" />]</span>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
context.addTemplate("sub", sub);
|
||||||
|
context.addTemplate("main", main);
|
||||||
|
|
||||||
|
const ctx = { numbers: [1, 2, 3], letters: ["a", "b"] };
|
||||||
|
const expected =
|
||||||
|
"<div> [1] [a] [x_1_a] [1] [b] [x_1_b] <span></span> [2] [a] [x_2_a] [2] [b] [x_2_b] <span></span> [3] [a] [x_3_a] [3] [b] [x_3_b] <span></span><span>[][][]</span></div>";
|
||||||
|
expect(context.renderToString("main", ctx)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("throws error if invalid loop expression", () => {
|
||||||
|
const test = `<div><t t-for="item" t-of="abc" t-key="item"><span t-key="item"/></t></div>`;
|
||||||
|
expect(() => renderToString(test)).toThrow("ctx.abc is not iterable");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-for with t-if inside", () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-for="{ id, text }" t-of="elems" t-key="id">
|
||||||
|
<span t-if="id lt 3"><t t-esc="text"/></span>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
const ctx = {
|
||||||
|
elems: [
|
||||||
|
{ id: 1, text: "a" },
|
||||||
|
{ id: 2, text: "b" },
|
||||||
|
{ id: 3, text: "c" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(renderToString(template, ctx)).toBe("<div><span>a</span><span>b</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-for with t-if inside (no external node)", () => {
|
||||||
|
const template = `
|
||||||
|
<t t-for="{ id, text }" t-of="elems" t-key="id">
|
||||||
|
<span t-if="id lt 3"><t t-esc="text"/></span>
|
||||||
|
</t>`;
|
||||||
|
const ctx = {
|
||||||
|
elems: [
|
||||||
|
{ id: 1, text: "a" },
|
||||||
|
{ id: 2, text: "b" },
|
||||||
|
{ id: 3, text: "c" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(renderToString(template, ctx)).toBe("<span>a</span><span>b</span>");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -122,6 +122,19 @@ describe("t-set", () => {
|
|||||||
expect(renderToString(template, { list: ["a", "b"] })).toBe(expected);
|
expect(renderToString(template, { list: ["a", "b"] })).toBe(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-set should reuse variable if possible: for..of", () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-set="v" t-value="1"/>
|
||||||
|
<div t-for="elem" t-of="list" t-key="elem_index">
|
||||||
|
<span>v<t t-esc="v"/></span>
|
||||||
|
<t t-set="v" t-value="elem"/>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
const expected = "<div><div><span>v1</span></div><div><span>va</span></div></div>";
|
||||||
|
expect(renderToString(template, { list: ["a", "b"] })).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
test("t-set with content and sub t-esc", () => {
|
test("t-set with content and sub t-esc", () => {
|
||||||
const template = `
|
const template = `
|
||||||
<div>
|
<div>
|
||||||
@@ -232,6 +245,22 @@ describe("t-set", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-set outside modified in t-for", async () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-set="iter" t-value="0"/>
|
||||||
|
<t t-for="val" t-of="['a','b']" t-key="val">
|
||||||
|
<p>InLoop: <t t-esc="iter"/></p>
|
||||||
|
<t t-set="iter" t-value="iter + 1"/>
|
||||||
|
</t>
|
||||||
|
<p>EndLoop: <t t-esc="iter"/></p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
expect(renderToString(template)).toBe(
|
||||||
|
"<div><p>InLoop: 0</p><p>InLoop: 1</p><p>EndLoop: 2</p></div>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("t-set outside modified in t-foreach increment-after operator", async () => {
|
test("t-set outside modified in t-foreach increment-after operator", async () => {
|
||||||
const template = `
|
const template = `
|
||||||
<div>
|
<div>
|
||||||
@@ -248,6 +277,22 @@ describe("t-set", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-set outside modified in t-for increment-after operator", async () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-set="iter" t-value="0"/>
|
||||||
|
<t t-for="val" t-of="['a','b']" t-key="val">
|
||||||
|
<p>InLoop: <t t-esc="iter"/></p>
|
||||||
|
<t t-set="iter" t-value="iter++"/>
|
||||||
|
</t>
|
||||||
|
<p>EndLoop: <t t-esc="iter"/></p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
expect(renderToString(template)).toBe(
|
||||||
|
"<div><p>InLoop: 0</p><p>InLoop: 0</p><p>EndLoop: 0</p></div>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("t-set outside modified in t-foreach increment-before operator", async () => {
|
test("t-set outside modified in t-foreach increment-before operator", async () => {
|
||||||
const template = `
|
const template = `
|
||||||
<div>
|
<div>
|
||||||
@@ -264,6 +309,22 @@ describe("t-set", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("t-set outside modified in t-for increment-before operator", async () => {
|
||||||
|
const template = `
|
||||||
|
<div>
|
||||||
|
<t t-set="iter" t-value="0"/>
|
||||||
|
<t t-for="val" t-of="['a','b']" t-key="val">
|
||||||
|
<p>InLoop: <t t-esc="iter"/></p>
|
||||||
|
<t t-set="iter" t-value="++iter"/>
|
||||||
|
</t>
|
||||||
|
<p>EndLoop: <t t-esc="iter"/></p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
expect(renderToString(template)).toBe(
|
||||||
|
"<div><p>InLoop: 0</p><p>InLoop: 1</p><p>EndLoop: 0</p></div>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("t-set can't alter from within callee", async () => {
|
test("t-set can't alter from within callee", async () => {
|
||||||
const context = new TestContext();
|
const context = new TestContext();
|
||||||
const sub = `<div><t t-esc="iter"/><t t-set="iter" t-value="'called'"/><t t-esc="iter"/></div>`;
|
const sub = `<div><t t-esc="iter"/><t t-set="iter" t-value="'called'"/><t t-esc="iter"/></div>`;
|
||||||
|
|||||||
@@ -0,0 +1,493 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`list of components components in a node in a t-for 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"item\\"]);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><ul><block-child-0/></ul></div>\`);
|
||||||
|
let block3 = createBlock(\`<li><block-child-0/></li>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of ctx['items']) {
|
||||||
|
const key1 = 'li_'+ctx['item'];
|
||||||
|
const b4 = comp1({item: ctx['item']}, key + \`__1__\${key1}\`, node, this, null);
|
||||||
|
c_block2[i1] = withKey(block3([], [b4]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components components in a node in a t-for 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['props'].item;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components crash on duplicate key in dev mode 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { OwlError, withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const keys1 = new Set();
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of [1,2]) {
|
||||||
|
const key1 = 'child';
|
||||||
|
if (keys1.has(String(key1))) { throw new OwlError(\`Got duplicate key in t-for: \${key1}\`)}
|
||||||
|
keys1.add(String(key1));
|
||||||
|
const props1 = {};
|
||||||
|
helpers.validateProps(\`Child\`, props1, this);
|
||||||
|
c_block1[i1] = withKey(comp1(props1, key + \`__1__\${key1}\`, node, this, null), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components crash on duplicate key in dev mode 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return text(\`\`);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components crash when using object as keys that serialize to the same string 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { OwlError, withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const keys1 = new Set();
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['item'] of [{},{}]) {
|
||||||
|
const key1 = ctx['item'];
|
||||||
|
if (keys1.has(String(key1))) { throw new OwlError(\`Got duplicate key in t-for: \${key1}\`)}
|
||||||
|
keys1.add(String(key1));
|
||||||
|
const props1 = {};
|
||||||
|
helpers.validateProps(\`Child\`, props1, this);
|
||||||
|
c_block1[i1] = withKey(comp1(props1, key + \`__1__\${key1}\`, node, this, null), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components crash when using object as keys that serialize to the same string 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return text(\`\`);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components list of sub components inside other nodes 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`SubComponent\`, true, false, false, []);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block3 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['blip'] of ctx['state'].blips) {
|
||||||
|
const key1 = ctx['blip'].id;
|
||||||
|
const b4 = comp1({}, key + \`__1__\${key1}\`, node, this, null);
|
||||||
|
c_block2[i1] = withKey(block3([], [b4]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components list of sub components inside other nodes 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span>asdf</span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return block1();
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components order is correct when slots are not of same type 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { capture, markRaw } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, true, false, []);
|
||||||
|
|
||||||
|
let block2 = createBlock(\`<div>A</div>\`);
|
||||||
|
|
||||||
|
function slot1(ctx, node, key = \\"\\") {
|
||||||
|
let b2;
|
||||||
|
if (!ctx['state'].active) {
|
||||||
|
b2 = block2();
|
||||||
|
}
|
||||||
|
return multi([b2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function slot2(ctx, node, key = \\"\\") {
|
||||||
|
return text(\`B\`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function slot3(ctx, node, key = \\"\\") {
|
||||||
|
return text(\`C\`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const ctx1 = capture(ctx);
|
||||||
|
return comp1({slots: markRaw({'a': {__render: slot1.bind(this), __ctx: ctx1, active: !ctx['state'].active}, 'b': {__render: slot2.bind(this), __ctx: ctx1, active: true}, 'c': {__render: slot3.bind(this), __ctx: ctx1, active: ctx['state'].active}})}, key + \`__1\`, node, this, null);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components order is correct when slots are not of same type 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { callSlot, withKey } = helpers;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['slotName'] of ctx['slotNames']) {
|
||||||
|
const key1 = ctx['slotName'];
|
||||||
|
const slot1 = (ctx['slotName']);
|
||||||
|
c_block1[i1] = withKey(toggler(slot1, callSlot(ctx, node, key1 + \`__1__\${key1}\`, slot1, true, {})), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components reconciliation alg works for t-for in t-for 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"blip\\"]);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['section'] of ctx['state'].s) {
|
||||||
|
const key1 = ctx['section'];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block3 = [];
|
||||||
|
let i2 = 0;
|
||||||
|
for (ctx['blip'] of ctx['section'].blips) {
|
||||||
|
const key2 = ctx['blip'];
|
||||||
|
c_block3[i2] = withKey(comp1({blip: ctx['blip']}, key + \`__1__\${key1}__\${key2}\`, node, this, null), key2);
|
||||||
|
i2++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
c_block2[i1] = withKey(list(c_block3), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components reconciliation alg works for t-for in t-for 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['props'].blip;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components reconciliation alg works for t-for in t-for, 2 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"row\\",\\"col\\"]);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
let block3 = createBlock(\`<p><block-child-0/></p>\`);
|
||||||
|
let block5 = createBlock(\`<p><block-child-0/></p>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['row'] of ctx['state'].rows) {
|
||||||
|
const key1 = ctx['row'];
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block4 = [];
|
||||||
|
let i2 = 0;
|
||||||
|
for (ctx['col'] of ctx['state'].cols) {
|
||||||
|
const key2 = ctx['col'];
|
||||||
|
const b6 = comp1({row: ctx['row'],col: ctx['col']}, key + \`__1__\${key1}__\${key2}\`, node, this, null);
|
||||||
|
c_block4[i2] = withKey(block5([], [b6]), key2);
|
||||||
|
i2++;
|
||||||
|
}
|
||||||
|
ctx = ctx.__proto__;
|
||||||
|
const b4 = list(c_block4);
|
||||||
|
c_block2[i1] = withKey(block3([], [b4]), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components reconciliation alg works for t-for in t-for, 2 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['props'].row+'_'+ctx['props'].col;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components simple list 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"value\\"]);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block1 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['elem'] of ctx['state'].elems) {
|
||||||
|
const key1 = ctx['elem'].id;
|
||||||
|
c_block1[i1] = withKey(comp1({value: ctx['elem'].value}, key + \`__1__\${key1}\`, node, this, null), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
return list(c_block1);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components simple list 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-text-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['props'].value;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components sub components rendered in a loop 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"n\\"]);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['number'] of ctx['state'].numbers) {
|
||||||
|
const key1 = ctx['number'];
|
||||||
|
c_block2[i1] = withKey(comp1({n: ctx['number']}, key + \`__1__\${key1}\`, node, this, null), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components sub components rendered in a loop 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<p><block-text-0/></p>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['props'].n;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components sub components with some state rendered in a loop 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, []);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['number'] of ctx['state'].numbers) {
|
||||||
|
const key1 = ctx['number'];
|
||||||
|
c_block2[i1] = withKey(comp1({}, key + \`__1__\${key1}\`, node, this, null), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components sub components with some state rendered in a loop 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<p><block-text-0/></p>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['state'].n;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components switch component position 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"key\\"]);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-child-0/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['c'] of ctx['clist']) {
|
||||||
|
const key1 = ctx['c'];
|
||||||
|
c_block2[i1] = withKey(comp1({key: ctx['c']}, key + \`__1__\${key1}\`, node, this, null), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components switch component position 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['props'].key;
|
||||||
|
return block1([txt1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components t-for with t-component, and update 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
let { withKey } = helpers;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"val\\"]);
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div><block-child-0/></div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
ctx = Object.create(ctx);
|
||||||
|
const c_block2 = [];
|
||||||
|
let i1 = 0;
|
||||||
|
for (ctx['n'] of [0,1]) {
|
||||||
|
const key1 = ctx['n'];
|
||||||
|
c_block2[i1] = withKey(comp1({val: ctx['n']}, key + \`__1__\${key1}\`, node, this, null), key1);
|
||||||
|
i1++;
|
||||||
|
}
|
||||||
|
const b2 = list(c_block2);
|
||||||
|
return block1([], [b2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`list of components t-for with t-component, and update 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<span><block-text-0/><block-text-1/></span>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let txt1 = ctx['state'].val;
|
||||||
|
let txt2 = ctx['props'].val;
|
||||||
|
return block1([txt1, txt2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
@@ -0,0 +1,391 @@
|
|||||||
|
import { App, Component, mount, onMounted, useState, xml } from "../../src/index";
|
||||||
|
import {
|
||||||
|
makeTestFixture,
|
||||||
|
nextAppError,
|
||||||
|
nextTick,
|
||||||
|
snapshotEverything,
|
||||||
|
useLogLifecycle,
|
||||||
|
} from "../helpers";
|
||||||
|
|
||||||
|
snapshotEverything();
|
||||||
|
|
||||||
|
let originalconsoleWarn = console.warn;
|
||||||
|
let mockConsoleWarn: any;
|
||||||
|
|
||||||
|
let fixture: HTMLElement;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = makeTestFixture();
|
||||||
|
mockConsoleWarn = jest.fn(() => {});
|
||||||
|
console.warn = mockConsoleWarn;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
console.warn = originalconsoleWarn;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("list of components", () => {
|
||||||
|
test("simple list", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<span><t t-esc="props.value"/></span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<t t-for="elem" t-of="state.elems" t-key="elem.id">
|
||||||
|
<Child value="elem.value"/>
|
||||||
|
</t>`;
|
||||||
|
static components = { Child };
|
||||||
|
|
||||||
|
state = useState({
|
||||||
|
elems: [
|
||||||
|
{ id: 1, value: "a" },
|
||||||
|
{ id: 2, value: "b" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<span>a</span><span>b</span>");
|
||||||
|
parent.state.elems.push({ id: 4, value: "d" });
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<span>a</span><span>b</span><span>d</span>");
|
||||||
|
|
||||||
|
parent.state.elems.pop();
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<span>a</span><span>b</span>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("components in a node in a t-for ", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div><t t-esc="props.item"/></div>`;
|
||||||
|
setup() {
|
||||||
|
useLogLifecycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<t t-for="item" t-of="items" t-key="'li_'+item">
|
||||||
|
<li>
|
||||||
|
<Child item="item"/>
|
||||||
|
</li>
|
||||||
|
</t>
|
||||||
|
</ul>
|
||||||
|
</div>`;
|
||||||
|
static components = { Child };
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
useLogLifecycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
get items() {
|
||||||
|
return [1, 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
"<div><ul><li><div>1</div></li><li><div>2</div></li></ul></div>"
|
||||||
|
);
|
||||||
|
expect([
|
||||||
|
"Parent:setup",
|
||||||
|
"Parent:willStart",
|
||||||
|
"Parent:willRender",
|
||||||
|
"Child:setup",
|
||||||
|
"Child:willStart",
|
||||||
|
"Child:setup",
|
||||||
|
"Child:willStart",
|
||||||
|
"Parent:rendered",
|
||||||
|
"Child:willRender",
|
||||||
|
"Child:rendered",
|
||||||
|
"Child:willRender",
|
||||||
|
"Child:rendered",
|
||||||
|
"Child:mounted",
|
||||||
|
"Child:mounted",
|
||||||
|
"Parent:mounted",
|
||||||
|
]).toBeLogged();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("reconciliation alg works for t-for in t-for", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div><t t-esc="props.blip"/></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-for="section" t-of="state.s" t-key="section">
|
||||||
|
<t t-for="blip" t-of="section.blips" t-key="blip">
|
||||||
|
<Child blip="blip"/>
|
||||||
|
</t>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
static components = { Child };
|
||||||
|
state = { s: [{ blips: ["a1", "a2"] }, { blips: ["b1"] }] };
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><div>a1</div><div>a2</div><div>b1</div></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("reconciliation alg works for t-for in t-for, 2", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div><t t-esc="props.row + '_' + props.col"/></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<p t-for="row" t-of="state.rows" t-key="row">
|
||||||
|
<p t-for="col" t-of="state.cols" t-key="col">
|
||||||
|
<Child row="row" col="col"/>
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
</div>`;
|
||||||
|
static components = { Child };
|
||||||
|
state = useState({ rows: [1, 2], cols: ["a", "b"] });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
"<div><p><p><div>1_a</div></p><p><div>1_b</div></p></p><p><p><div>2_a</div></p><p><div>2_b</div></p></p></div>"
|
||||||
|
);
|
||||||
|
parent.state.rows = [2, 1];
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
"<div><p><p><div>2_a</div></p><p><div>2_b</div></p></p><p><p><div>1_a</div></p><p><div>1_b</div></p></p></div>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("sub components rendered in a loop", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<p><t t-esc="props.n"/></p>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-for="number" t-of="state.numbers" t-key="number" >
|
||||||
|
<Child n="number"/>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
static components = { Child };
|
||||||
|
|
||||||
|
state = useState({ numbers: [1, 2, 3] });
|
||||||
|
}
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
|
||||||
|
expect(fixture.innerHTML).toBe(`<div><p>1</p><p>2</p><p>3</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("sub components with some state rendered in a loop", async () => {
|
||||||
|
let n = 1;
|
||||||
|
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<p><t t-esc="state.n"/></p>`;
|
||||||
|
state: any;
|
||||||
|
setup() {
|
||||||
|
this.state = useState({ n });
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-for="number" t-of="state.numbers" t-key="number">
|
||||||
|
<Child/>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
static components = { Child };
|
||||||
|
|
||||||
|
state = useState({
|
||||||
|
numbers: [1, 2, 3],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
|
||||||
|
parent.state.numbers = [1, 3];
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe(`<div><p>1</p><p>3</p></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("list of sub components inside other nodes", async () => {
|
||||||
|
// this confuses the patching algorithm...
|
||||||
|
class SubComponent extends Component {
|
||||||
|
static template = xml`<span>asdf</span>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<div t-for="blip" t-of="state.blips" t-key="blip.id">
|
||||||
|
<SubComponent />
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
static components = { SubComponent };
|
||||||
|
state = useState({
|
||||||
|
blips: [
|
||||||
|
{ a: "a", id: 1 },
|
||||||
|
{ b: "b", id: 2 },
|
||||||
|
{ c: "c", id: 4 },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
"<div><div><span>asdf</span></div><div><span>asdf</span></div><div><span>asdf</span></div></div>"
|
||||||
|
);
|
||||||
|
parent.state.blips.splice(0, 1);
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe(
|
||||||
|
"<div><div><span>asdf</span></div><div><span>asdf</span></div></div>"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("t-for with t-component, and update", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<span>
|
||||||
|
<t t-esc="state.val"/>
|
||||||
|
<t t-esc="props.val"/>
|
||||||
|
</span>`;
|
||||||
|
state = useState({ val: "A" });
|
||||||
|
setup() {
|
||||||
|
onMounted(() => {
|
||||||
|
this.state.val = "B";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class Parent extends Component {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-for="n" t-of="[0, 1]" t-key="n">
|
||||||
|
<Child val="n"/>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>A0</span><span>A1</span></div>");
|
||||||
|
|
||||||
|
await nextTick(); // wait for changes triggered in mounted to be applied
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>B0</span><span>B1</span></div>");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("switch component position", async () => {
|
||||||
|
const childInstances = [];
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`<div t-esc="props.key"></div>`;
|
||||||
|
setup() {
|
||||||
|
childInstances.push(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`<span>
|
||||||
|
<t t-for="c" t-of="clist" t-key="c">
|
||||||
|
<Child key="c"/>
|
||||||
|
</t>
|
||||||
|
</span>`;
|
||||||
|
|
||||||
|
clist = [1, 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<span><div>1</div><div>2</div></span>");
|
||||||
|
parent.clist = [2, 1];
|
||||||
|
parent.render();
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<span><div>2</div><div>1</div></span>");
|
||||||
|
expect(childInstances.length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("crash on duplicate key in dev mode", async () => {
|
||||||
|
const consoleInfo = console.info;
|
||||||
|
console.info = jest.fn();
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml``;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<t t-for="item" t-of="[1, 2]" t-key="'child'">
|
||||||
|
<Child/>
|
||||||
|
</t>
|
||||||
|
`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App(Parent, { test: true });
|
||||||
|
const mountProm = expect(app.mount(fixture)).rejects.toThrow(
|
||||||
|
"Got duplicate key in t-for: child"
|
||||||
|
);
|
||||||
|
await expect(nextAppError(app)).resolves.toThrow("Got duplicate key in t-for: child");
|
||||||
|
await mountProm;
|
||||||
|
console.info = consoleInfo;
|
||||||
|
expect(mockConsoleWarn).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("crash when using object as keys that serialize to the same string", async () => {
|
||||||
|
const consoleInfo = console.info;
|
||||||
|
console.info = jest.fn();
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml``;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<t t-for="item" t-of="[{}, {}]" t-key="item">
|
||||||
|
<Child/>
|
||||||
|
</t>
|
||||||
|
`;
|
||||||
|
static components = { Child };
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App(Parent, { test: true });
|
||||||
|
const mountProm = expect(app.mount(fixture)).rejects.toThrow(
|
||||||
|
"Got duplicate key in t-for: [object Object]"
|
||||||
|
);
|
||||||
|
await expect(nextAppError(app)).resolves.toThrow("Got duplicate key in t-for: [object Object]");
|
||||||
|
await mountProm;
|
||||||
|
console.info = consoleInfo;
|
||||||
|
expect(mockConsoleWarn).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("order is correct when slots are not of same type", async () => {
|
||||||
|
class Child extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<t t-slot="{{ slotName }}" t-for="slotName" t-of="slotNames" t-key="slotName"/>
|
||||||
|
`;
|
||||||
|
get slotNames() {
|
||||||
|
return Object.entries(this.props.slots)
|
||||||
|
.filter((entry: any) => entry[1].active)
|
||||||
|
.map((entry) => entry[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static template = xml`
|
||||||
|
<Child>
|
||||||
|
<t t-set-slot="a" active="!state.active"><div t-if="!state.active">A</div></t>
|
||||||
|
<t t-set-slot="b" active="true">B</t>
|
||||||
|
<t t-set-slot="c" active="state.active">C</t>
|
||||||
|
</Child>
|
||||||
|
`;
|
||||||
|
static components = { Child };
|
||||||
|
state = useState({ active: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parent = await mount(Parent, fixture);
|
||||||
|
expect(fixture.textContent).toBe("AB");
|
||||||
|
parent.state.active = true;
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.textContent).toBe("BC");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user