FIX: Support getters in hbr #each context ()

There is a risk of overriding and then deleting a prop of the context in case of a naming clash between localName and that prop, e.g.

```js
class Test {
  item = "foo";
  items = [1, 2];
}

const template = `
  {{#each items as |item|}}
    {{item}}
  {{/each}}
`;
const compiledTemplate = compile(template);

const object = new Test();
// object.item === "foo"
const output = compiledTemplate(object, RUNTIME_OPTIONS);
// object.item === undefined
```

…but I think we can accept this risk and just be careful.`#each` isn't widely used in hbr anyway (as proven by the other long-standing and recently fixed bug) and hbr is on its way out anyway.
This commit is contained in:
Jarek Radosz 2024-09-17 12:07:07 +02:00 committed by GitHub
parent bee8214399
commit 7b8343d482
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 25 additions and 1 deletions
app/assets/javascripts
discourse-common/addon/lib
discourse/tests/integration/helpers

@ -44,11 +44,12 @@ export function registerRawHelpers(hbs, handlebarsClass, owner) {
}
let list = get(this, contextName);
let output = [];
let innerContext = { ...options.contexts[0] };
let innerContext = options.contexts[0];
for (let i = 0; i < list.length; i++) {
innerContext[localName] = list[i];
output.push(options.fn(innerContext));
}
delete innerContext[localName];
return output.join("");
}
);

@ -8,6 +8,7 @@ import raw from "discourse/helpers/raw";
import rawRenderGlimmer from "discourse/lib/raw-render-glimmer";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { compile } from "discourse-common/lib/raw-handlebars";
import { RUNTIME_OPTIONS } from "discourse-common/lib/raw-handlebars-helpers";
import {
addRawTemplate,
removeRawTemplate,
@ -121,4 +122,26 @@ module("Integration | Helper | raw", function (hooks) {
assert.dom("span").hasText("foo 1 foo 2");
});
test("#each helper handles getters", async function (assert) {
const template = `
{{#each items as |item|}}
{{string}} {{item}}
{{/each}}
`;
const compiledTemplate = compile(template);
class Test {
items = [1, 2];
get string() {
return "foo";
}
}
const object = new Test();
const output = compiledTemplate(object, RUNTIME_OPTIONS);
assert.true(/\s*foo 1\s*foo 2\s*/.test(output));
});
});