diff --git a/lib/handlebars/helpers/each.js b/lib/handlebars/helpers/each.js
index 914928d6d..f2e935f50 100644
--- a/lib/handlebars/helpers/each.js
+++ b/lib/handlebars/helpers/each.js
@@ -19,12 +19,13 @@ export default function(instance) {
data = createFrame(options.data);
}
- function execIteration(field, index, last) {
+ function execIteration(field, index, last, value) {
if (data) {
data.key = field;
data.index = index;
data.first = index === 0;
data.last = !!last;
+ data.value = value;
}
ret = ret + fn(context[field], {
@@ -37,7 +38,7 @@ 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, i, i === context.length - 1, context[i]);
}
}
} else {
@@ -49,14 +50,14 @@ 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, i - 1, false, context[priorKey]);
}
priorKey = key;
i++;
}
}
if (priorKey !== undefined) {
- execIteration(priorKey, i - 1, true);
+ execIteration(priorKey, i - 1, true, context[priorKey]);
}
}
}
diff --git a/spec/builtins.js b/spec/builtins.js
index dc1df0a3e..d620ddf09 100644
--- a/spec/builtins.js
+++ b/spec/builtins.js
@@ -115,6 +115,67 @@ describe('builtin helpers', function() {
shouldCompileTo(string, {goodbyes: {}, world: 'world'}, 'cruel world!');
});
+ it('each with an object and @key/@value', function() {
+ var string = '{{#each goodbyes}}{{@key}}. {{@value.text}}! {{/each}}cruel {{world}}!';
+
+ function Clazz() {
+ this['#1'] = {text: 'goodbye'};
+ this[2] = {text: 'GOODBYE'};
+ }
+ Clazz.prototype.foo = 'fail';
+ var hash = {goodbyes: new Clazz(), world: 'world'};
+
+ // Object property iteration order is undefined according to ECMA spec,
+ // so we need to check both possible orders
+ // @see http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop
+ var actual = compileWithPartials(string, hash);
+ var expected1 = '<b>#1</b>. goodbye! 2. GOODBYE! cruel world!';
+ var expected2 = '2. GOODBYE! <b>#1</b>. goodbye! cruel world!';
+
+ equals(actual === expected1 || actual === expected2, true, 'each with object argument iterates over the contents when not empty');
+ shouldCompileTo(string, {goodbyes: {}, world: 'world'}, 'cruel world!');
+ });
+
+ it('each with an object and @key/@value that has key/value properties itself', function() {
+ var string = '{{#each goodbyes}}{{@key}}. [{{@value.key}}] {{@value.value}}! {{/each}}cruel {{world}}!';
+
+ function Clazz() {
+ this['#1'] = {value: 'goodbye', key: 'html'};
+ this[2] = {value: 'GOODBYE', key: 'number'};
+ }
+ Clazz.prototype.foo = 'fail';
+ var hash = {goodbyes: new Clazz(), world: 'world'};
+
+ // Object property iteration order is undefined according to ECMA spec,
+ // so we need to check both possible orders
+ // @see http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop
+ var actual = compileWithPartials(string, hash);
+ var expected1 = '<b>#1</b>. [html] goodbye! 2. [number] GOODBYE! cruel world!';
+ var expected2 = '2. [number] GOODBYE! <b>#1</b>. [html] goodbye! cruel world!';
+ equals(actual === expected1 || actual === expected2, true, 'each with object argument iterates over the contents when not empty');
+ shouldCompileTo(string, {goodbyes: {}, world: 'world'}, 'cruel world!');
+ });
+
+ it('each with @key', function() {
+ var string = '{{#each goodbyes}}{{@key}}. {{text}}! {{/each}}cruel {{world}}!';
+ var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
+
+ var template = CompilerContext.compile(string);
+ var result = template(hash);
+
+ equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @key variable is used');
+ });
+
+ it('each with @key/@value', function() {
+ var string = '{{#each goodbyes}}{{@key}}. {{@value.text}}! {{/each}}cruel {{world}}!';
+ var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};
+
+ var template = CompilerContext.compile(string);
+ var result = template(hash);
+
+ equal(result, '0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!', 'The @key/@value variables are used');
+ });
+
it('each with @index', function() {
var string = '{{#each goodbyes}}{{@index}}. {{text}}! {{/each}}cruel {{world}}!';
var hash = {goodbyes: [{text: 'goodbye'}, {text: 'Goodbye'}, {text: 'GOODBYE'}], world: 'world'};