From 610ed023732fa20e7f668ae7f83984a6a6d43bec Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Wed, 9 Aug 2023 07:38:10 +0200 Subject: [PATCH] [FIX] runtime: properly support t-foreach on strings 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: odoo/owl#1503 --- src/runtime/template_helpers.ts | 12 ++++---- .../__snapshots__/t_foreach.test.ts.snap | 28 +++++++++++++++++++ tests/compiler/t_foreach.test.ts | 9 ++++++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/runtime/template_helpers.ts b/src/runtime/template_helpers.ts index 72d7ff88..73399446 100644 --- a/src/runtime/template_helpers.ts +++ b/src/runtime/template_helpers.ts @@ -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 = [...(>collection)]; + values = keys; } else if (collection && typeof collection === "object") { - if (Symbol.iterator in collection) { - keys = [...(>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`); } diff --git a/tests/compiler/__snapshots__/t_foreach.test.ts.snap b/tests/compiler/__snapshots__/t_foreach.test.ts.snap index 0483f41e..4d56da5d 100644 --- a/tests/compiler/__snapshots__/t_foreach.test.ts.snap +++ b/tests/compiler/__snapshots__/t_foreach.test.ts.snap @@ -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 ) { diff --git a/tests/compiler/t_foreach.test.ts b/tests/compiler/t_foreach.test.ts index 3a17483f..4ae88dc7 100644 --- a/tests/compiler/t_foreach.test.ts +++ b/tests/compiler/t_foreach.test.ts @@ -131,6 +131,15 @@ describe("t-foreach", () => { expect(renderToString(template, context)).toBe(expected); }); + test("iterate, string param", () => { + const template = ` + + [: ] + `; + const expected = ` [0: a a] [1: b b] [2: c c] `; + expect(renderToString(template)).toBe(expected); + }); + test("iterate, iterable param", () => { const template = `