diff --git a/packages/ember-htmlbars/lib/helpers/each.js b/packages/ember-htmlbars/lib/helpers/each.js
index 21d146c7ab6..fcd22884a8b 100644
--- a/packages/ember-htmlbars/lib/helpers/each.js
+++ b/packages/ember-htmlbars/lib/helpers/each.js
@@ -51,12 +51,7 @@ import decodeEachKey from "ember-htmlbars/utils/decode-each-key";
items (and reorder the generated DOM elements) based on each item's `id`
property.
- There are a few special values for `key`:
-
- * `@index` - The index of the item in the array.
- * `@item` - The item in the array itself. This can only be used for arrays of strings
- or numbers.
- * `@guid` - Generate a unique identifier for each object (uses `Ember.guidFor`).
+ By default the item's own reference is used.
### {{else}} condition
diff --git a/packages/ember-htmlbars/lib/utils/decode-each-key.js b/packages/ember-htmlbars/lib/utils/decode-each-key.js
index faa09d81674..06f4be4c85e 100644
--- a/packages/ember-htmlbars/lib/utils/decode-each-key.js
+++ b/packages/ember-htmlbars/lib/utils/decode-each-key.js
@@ -1,26 +1,42 @@
-import Ember from "ember-metal/core";
+import Ember from 'ember-metal/core';
import { get } from "ember-metal/property_get";
import { guidFor } from "ember-metal/utils";
+function identity(item) {
+ let key;
+ let type = typeof item;
+
+ if (type === 'string' || type === 'number') {
+ key = item;
+ } else {
+ key = guidFor(item);
+ }
+
+ return key;
+}
export default function decodeEachKey(item, keyPath, index) {
- var key;
+ var key, deprecatedSpecialKey;
switch (keyPath) {
case '@index':
key = index;
break;
case '@guid':
+ deprecatedSpecialKey = '@guid';
key = guidFor(item);
break;
case '@item':
+ deprecatedSpecialKey = '@item';
key = item;
break;
+ case '@identity':
+ key = identity(item);
+ break;
default:
if (keyPath) {
key = get(item, keyPath);
} else {
- Ember.warn('Using `{{each}}` without specifying a key can lead to unusual behavior. Please specify a `key` that identifies a unique value on each item being iterated. E.g. `{{each model key="@guid" as |item|}}`.');
- key = index;
+ key = identity(item);
}
}
@@ -28,5 +44,7 @@ export default function decodeEachKey(item, keyPath, index) {
key = String(key);
}
+ Ember.deprecate(`Using '${deprecatedSpecialKey}' with the {{each}} helper, is deprecated. Switch to '@identity' or remove 'key=' from your template.`, !deprecatedSpecialKey);
+
return key;
}
diff --git a/packages/ember-htmlbars/tests/helpers/each_test.js b/packages/ember-htmlbars/tests/helpers/each_test.js
index a12713e1fc7..39b273b3d46 100644
--- a/packages/ember-htmlbars/tests/helpers/each_test.js
+++ b/packages/ember-htmlbars/tests/helpers/each_test.js
@@ -1226,7 +1226,9 @@ QUnit.test('can specify `@index` to represent the items index in the array being
equal(view.$().text(), '123');
});
-QUnit.test('can specify `@guid` to represent the items GUID', function() {
+QUnit.test('can specify `@guid` to represent the items GUID [DEPRECATED]', function() {
+ expectDeprecation(`Using '@guid' with the {{each}} helper, is deprecated. Switch to '@identity' or remove 'key=' from your template.`);
+
runDestroy(view);
view = EmberView.create({
items: [
@@ -1243,6 +1245,8 @@ QUnit.test('can specify `@guid` to represent the items GUID', function() {
});
QUnit.test('can specify `@item` to represent primitive items', function() {
+ expectDeprecation(`Using '@item' with the {{each}} helper, is deprecated. Switch to '@identity' or remove 'key=' from your template.`);
+
runDestroy(view);
view = EmberView.create({
items: [1, 2, 3],
@@ -1260,5 +1264,41 @@ QUnit.test('can specify `@item` to represent primitive items', function() {
equal(view.$().text(), 'foobarbaz');
});
+QUnit.test('can specify `@identity` to represent primitive items', function() {
+ runDestroy(view);
+ view = EmberView.create({
+ items: [1, 2, 3],
+ template: compile("{{#each view.items key='@identity' as |item|}}{{item}}{{/each}}")
+ });
+
+ runAppend(view);
+
+ equal(view.$().text(), '123');
+
+ run(function() {
+ set(view, 'items', ['foo', 'bar', 'baz']);
+ });
+
+ equal(view.$().text(), 'foobarbaz');
+});
+
+QUnit.test('can specify `@identity` to represent mixed object and primitive items', function() {
+ runDestroy(view);
+ view = EmberView.create({
+ items: [1, { id: 2 }, 3],
+ template: compile("{{#each view.items key='@identity' as |item|}}{{#if item.id}}{{item.id}}{{else}}{{item}}{{/if}}{{/each}}")
+ });
+
+ runAppend(view);
+
+ equal(view.$().text(), '123');
+
+ run(function() {
+ set(view, 'items', ['foo', { id: 'bar' }, 'baz']);
+ });
+
+ equal(view.$().text(), 'foobarbaz');
+});
+
testEachWithItem("{{#each foo in bar}}", false);
testEachWithItem("{{#each bar as |foo|}}", true);