diff --git a/lib/handlebars/helpers/each.js b/lib/handlebars/helpers/each.js index c1fa20f39..db7e9cdc8 100644 --- a/lib/handlebars/helpers/each.js +++ b/lib/handlebars/helpers/each.js @@ -33,7 +33,7 @@ export default function(instance) { data = createFrame(options.data); } - function execIteration(field, index, last) { + function execIteration(field, value, index, last) { if (data) { data.key = field; data.index = index; @@ -47,12 +47,9 @@ export default function(instance) { ret = ret + - fn(context[field], { + fn(value, { data: data, - blockParams: blockParams( - [context[field], field], - [contextPath + field, null] - ) + blockParams: blockParams([value, field], [contextPath + field, null]) }); } @@ -60,9 +57,15 @@ export default function(instance) { if (isArray(context)) { for (let j = context.length; i < j; i++) { if (i in context) { - execIteration(i, i, i === context.length - 1); + execIteration(i, context[i], i, i === context.length - 1); } } + } else if (context instanceof Map) { + const j = context.size - 1; + for (const [key, value] of context) { + execIteration(key, value, i, i === j); + i++; + } } else if (global.Symbol && context[global.Symbol.iterator]) { const newContext = []; const iterator = context[global.Symbol.iterator](); @@ -71,7 +74,7 @@ export default function(instance) { } context = newContext; for (let j = context.length; i < j; i++) { - execIteration(i, i, i === context.length - 1); + execIteration(i, context[i], i, i === context.length - 1); } } else { let priorKey; @@ -81,13 +84,13 @@ export default function(instance) { // the last iteration without have to scan the object twice and create // an itermediate keys array. if (priorKey !== undefined) { - execIteration(priorKey, i - 1); + execIteration(priorKey, context[priorKey], i - 1); } priorKey = key; i++; }); if (priorKey !== undefined) { - execIteration(priorKey, i - 1, true); + execIteration(priorKey, context[priorKey], i - 1, true); } } } diff --git a/lib/handlebars/runtime.js b/lib/handlebars/runtime.js index 67a500d79..5fb86b66e 100644 --- a/lib/handlebars/runtime.js +++ b/lib/handlebars/runtime.js @@ -127,6 +127,9 @@ export function template(templateSpec, env) { return obj[name]; }, lookupProperty: function(parent, propertyName) { + if (parent instanceof Map) { + return parent.get(propertyName); + } let result = parent[propertyName]; if (result == null) { return result; diff --git a/spec/basic.js b/spec/basic.js index 65d228729..5b8051072 100644 --- a/spec/basic.js +++ b/spec/basic.js @@ -394,6 +394,17 @@ describe('basic context', function() { ); }); + it('nested paths with Map', function() { + var map = new Map(); + map.set('expression', 'beautiful'); + shouldCompileTo( + 'Goodbye {{foo/expression}} world!', + { foo: map }, + 'Goodbye beautiful world!', + 'Nested paths access nested objects' + ); + }); + it('nested paths with empty string value', function() { shouldCompileTo( 'Goodbye {{alan/expression}} world!', diff --git a/spec/builtins.js b/spec/builtins.js index c74092e1b..1ce412781 100644 --- a/spec/builtins.js +++ b/spec/builtins.js @@ -551,6 +551,29 @@ describe('builtin helpers', function() { ); }); } + + it('each on Map and @key', function() { + var goodbyes = new Map(); + goodbyes.set('first', 'goodbye'); + goodbyes.set('second', 'Goodbye'); + goodbyes.set('last', 'GOODBYE'); + var goodbyesEmpty = new Map(); + var string = + '{{#each goodbyes}}{{@key}}. {{.}}! {{/each}}cruel {{world}}!'; + var hash = { goodbyes: goodbyes, world: 'world' }; + shouldCompileTo( + string, + hash, + 'first. goodbye! second. Goodbye! last. GOODBYE! cruel world!', + 'each with map argument iterates over entries when not empty' + ); + shouldCompileTo( + string, + { goodbyes: goodbyesEmpty, world: 'world' }, + 'cruel world!', + 'each with map argument ignores the contents when empty' + ); + }); }); describe('#log', function() {