handlebars-lang / handlebars-lang/handlebars.js

Extend Each helper's support for Maps using `as | value key |`

Open
#2,030 0 comments 0 reactions 0 assignees View on GitHub
feature
Dominant language
JavaScript
Stars
18.7k
Forks
2.1k
PR merge metrics
No merged PRs in 30d

Description

Due to the way that the Map handler of the each function is currently done the value is returned as a multi dimensional array.
https://github.com/handlebars-lang/handlebars.js/blob/25c696b8891e65f7f7e8405ecdf0a6b17808d237/lib/handlebars/helpers/each.js#L47-L51

During the `execIteration` function the Map value getter is now called as such:
https://github.com/handlebars-lang/handlebars.js/blob/25c696b8891e65f7f7e8405ecdf0a6b17808d237/lib/handlebars/helpers/each.js#L36

However a `Map` doesn't support index signatures, thus `context[field] === undefined` and instead needs to be called using `Map#get`.

In the case of the following handlebars code w/ appropriate map:
```js
var aMapObject = new Map([["a",true],["b",false],["c",true]]);
```
```hbs
{{#each aMapObject}}
{{log this @key}}
{{/each}}
```
the result of the log would be `["a",true], 0` while I'd expect `this` to be `true` and `@key` to be `"a"` like it is when you call each on an Object.

The solution would be to on [Line 36 of each.js](https://github.com/handlebars-lang/handlebars.js/blob/25c696b8891e65f7f7e8405ecdf0a6b17808d237/lib/handlebars/helpers/each.js#L36) call `context.get(field)` instead of `context[field]`. Of course it'd have to know for sure it was a Map at that point.

So it could be resolved either by adding a property to `execIteration` of `isMap` (or perhaps a more generic solution with calling a function on context) to determine whether it should use the index signature or call .get or some other function.

Or alternatively; if context is a Map, transform it into an Object Record instead.

---

I have already resolved this locally, but thought I'd share this here in case someone wants to pick this up and add it to Handlebars itself.
I resolved this by overwriting the each helper, checking if the `context` is a map, and if so using `context.get(field)` instead of `context[field]`. If it isn't a map, parse it using the original Handlebars each helper.

Contributor guide

Open the contributing guide

Research direction

Start in lib/handlebars/helpers/each.js at the linked execIteration and Map-handling lines, then reproduce the provided Map and Handlebars example. The work is done when iterating the Map exposes each entry's value as this and its string key through @key, without changing ordinary object iteration.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
web-dev
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.