Skip to content

Commit

Permalink
[FIX] runtime: properly support t-foreach on strings
Browse files Browse the repository at this point in the history
Previously, support for iterables was added to t-foreach. The idea was
that anything that you can spread or on which you can use for..of would
be supported. Due to an implementation mistakes, strings, which are
iterable were not supported because we checked that the typeof the
iterable was 'object'.

To fix this, we coerce the iterable to an object and check whether that
coerced value has a Symbol.iterator property, which is what happens
behind the scenes when using for..of or spreading a primitive.

Closes: #1503
  • Loading branch information
sdegueldre authored and brboi committed Aug 25, 2023
1 parent 4b23f51 commit 610ed02
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 7 deletions.
12 changes: 5 additions & 7 deletions src/runtime/template_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,12 @@ function prepareList(collection: unknown): [unknown[], unknown[], number, undefi
} else if (collection instanceof Map) {
keys = [...collection.keys()];
values = [...collection.values()];
} else if (Symbol.iterator in Object(collection)) {
keys = [...(<Iterable<unknown>>collection)];
values = keys;
} else if (collection && typeof collection === "object") {
if (Symbol.iterator in collection) {
keys = [...(<Iterable<unknown>>collection)];
values = keys;
} else {
values = Object.values(collection);
keys = Object.keys(collection);
}
values = Object.values(collection);
keys = Object.keys(collection);
} else {
throw new OwlError(`Invalid loop expression: "${collection}" is not iterable`);
}
Expand Down
28 changes: 28 additions & 0 deletions tests/compiler/__snapshots__/t_foreach.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,34 @@ exports[`t-foreach iterate, position 1`] = `
}"
`;
exports[`t-foreach iterate, string param 1`] = `
"function anonymous(app, bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, withKey } = helpers;
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList('abc');;
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`item\`] = k_block1[i1];
ctx[\`item_index\`] = i1;
ctx[\`item_value\`] = v_block1[i1];
const key1 = ctx['item_index'];
const b3 = text(\` [\`);
const b4 = text(ctx['item_index']);
const b5 = text(\`: \`);
const b6 = text(ctx['item']);
const b7 = text(\` \`);
const b8 = text(ctx['item_value']);
const b9 = text(\`] \`);
c_block1[i1] = withKey(multi([b3, b4, b5, b6, b7, b8, b9]), key1);
}
return list(c_block1);
}
}"
`;
exports[`t-foreach simple iteration (in a node) 1`] = `
"function anonymous(app, bdom, helpers
) {
Expand Down
9 changes: 9 additions & 0 deletions tests/compiler/t_foreach.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ describe("t-foreach", () => {
expect(renderToString(template, context)).toBe(expected);
});

test("iterate, string param", () => {
const template = `
<t t-foreach="'abc'" t-as="item" t-key="item_index">
[<t t-esc="item_index"/>: <t t-esc="item"/> <t t-esc="item_value"/>]
</t>`;
const expected = ` [0: a a] [1: b b] [2: c c] `;
expect(renderToString(template)).toBe(expected);
});

test("iterate, iterable param", () => {
const template = `
<t t-foreach="map.values()" t-as="item" t-key="item_index">
Expand Down

0 comments on commit 610ed02

Please sign in to comment.