handlebars-lang / handlebars-lang/handlebars.js
Extend Each helper's support for Maps using `as | value key |`
- 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
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