handlebars-lang / handlebars.js

Minimal templating on steroids.
http://handlebarsjs.com
MIT License
17.81k stars 2.04k forks source link

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

Open dylanpiera opened 4 months ago

dylanpiera commented 4 months ago

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:

var aMapObject = new Map([["a",true],["b",false],["c",true]]);
{{#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 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.