diff --git a/assets/docs/enumerables.md b/assets/docs/enumerables.md new file mode 100644 index 0000000000..a3f18789f0 --- /dev/null +++ b/assets/docs/enumerables.md @@ -0,0 +1,217 @@ +## The Ember Enumerable API + +### What Are Enumerables? + +In Ember, an Enumerable is any object that contains a number of child objects, and which allows you to work with those children using the Enumerable interface. The most basic Enumerable is the built-in JavaScript Array. + +For instance, all Enumerables support the standard `forEach` method: + +
+[1,2,3].forEach(function(item) { + console.log(item); +}); ++ +In general, Enumerable methods, like `forEach`, take an optional second parameter, which will become the value of `this` in the callback function: + +
+var array = [1,2,3]; + +array.forEach(function(item) { + console.log(item, this.indexOf(item)); +}, array) ++ +Among other reasons, you will find this useful when using another Enumerable method as a callback to `forEach`: + +
+var array = [1,2,3]; + +array.forEach(array.removeObject, array); ++ +NOTE: This second parameter helps work around a limitation of JavaScript which sets the value of `this` to `window` in methods used this way. + +### Enumerables in Ember + +In general, Ember objects that represent lists implement the Enumerable interface. Some examples: + + * *Array*: Ember extends the native JavaScript Array with the Enumerable interface. + * *ArrayProxy*: A construct that wraps a native Array and adds additional functionality for the view layer. + * *Set*: An object that can quickly answer whether it includes an object. + +### The Enumerable Interface + +#### Parameters + +The callbacks to Enumerable methods take three arguments: + + * *item*: the item for the current iteration. + * *index*: an Integer, counting up from 0. + * *self*: the Enumerable itself. + +#### Enumeration + +To enumerate all the values of an enumerable object, use the `forEach` method: + +
+enumerable.forEach(function(item, index, self) { + console.log(item); +}); ++ +To invoke some method on each element of an enumerable object, use the `invoke` method: + +
+Person = Ember.Object.extend({ + sayHello: function() { + console.log("Hello from " + this.get('name')); + } +}); + +var people = [ + Person.create({ name: "Juan" }), + Person.create({ name: "Charles" }), + Person.create({ name: "Majd" }) +] + +people.invoke('sayHello'); + +// Hello from Juan +// Hello from Charles +// Hello from Majd ++ +#### First and Last + +You can get the first or last object from an Enumerable by getting `firstObject` or `lastObject`. + +
+[1,2,3].get('firstObject') // 1 +[1,2,3].get('lastObject') // 3 ++ +#### Converting to Array + +This one is simple. To convert an Enumerable into an Array, simply call its `toArray` method. + +#### Transforming + +You can transform an Enumerable into a derived Array by using the `map` method: + +
+['Goodbye', 'cruel', 'world'].map(function(item, index, self) { + return item + "!"; +}); + +// returns ["Goodbye!", "cruel!", "world!"] ++ +#### Setting and Getting on Each Object + +A very common use of `forEach` and `map` is to get (or set) a property on each element. You can use the `getEach` and `setEach` methods to accomplish these goals. + +
+var arr = [Ember.Object.create(), Ember.Object.create()]; + +// we now have an Array containing two Ember.Objects + +arr.setEach('name', 'unknown'); +arr.getEach('name') // ['unknown', 'unknown'] ++ +#### Filtering + +Another common task to perform on an Enumerable is to take the Enumerable as input, and return an Array after sorting or filtering it based on some criteria. + +For arbitrary filtering, use the (you guessed it) `filter` method. The filter method expects the callback to return `true` if Ember should include it in the final Array, and `false` or `undefined` if Ember should not. + +
+var arr = [1,2,3,4,5]; + +arr.filter(function(item, index, self) { + if(item < 4) { return true; } +}) + +// returns [1,2,3] ++ +When working with a collection of Ember objects, you will often want to filter a set of objects based upon the value of some property. The `filterProperty` method provides a shortcut. + +
+Todo = Ember.Object.extend({ + title: null, + isDone: false +}); + +todos = [ + Todo.create({ title: 'Write code', isDone: true }), + Todo.create({ title: 'Go to sleep' }) +]; + +todos.filterProperty('isDone', true); + +// returns an Array containing just the first item ++ +If you want to return just the first matched value, rather than an Array containing all of the matched values, you can use `find` and `findProperty`, which work just like `filter` and `filterProperty`, but return only one item. + +#### Sorting + +You can sort an Enumerable based on the value of some property or list of properties using `sortProperty`. If you pass in multiple properties, Ember will sort items with the same value for the first property by the value of the second parameter, and so on. + +
+var todos = [ + Todo.create({ title: 'Write code', isDone: true }), + Todo.create({ title: 'Go to sleep' }), + Todo.create({ title: 'Eat lunch', isDone: true }) +]; + +todos.sortProperty('isDone', 'title'); + +// returns an Array containing +// * Go to sleep +// * Eat lunch +// * Write code ++ +Internally, the `sortProperty` method uses `Ember.compare`, which uses Ember's comparable semantics. You can override the default comparison behavior for a custom object by using the `Ember.Comparable` mixin. + +#### Aggregate Information (All or Any) + +If you want to find out whether every item in an Enumerable matches some condition, you can use the `every` method: + +
+Person = Ember.Object.extend({ + name: null, + isHappy: false +}); + +var people = [ + Person.create({ name: 'Yehuda', isHappy: true }), + Person.create({ name: 'Majd', isHappy: false }) +]; + +people.every(function(person, index, self) { + if(person.get('isHappy')) { return true; } +}); + +// returns false ++ +If you want to find out whether at least one item in an Enumerable matches some conditions, you can use the `some` method: + +
+people.some(function(person, index, self) { + if(person.get('isHappy')) { return true; } +}); + +// returns true ++ +Just like the filtering methods, the `every` and `some` methods have analogous `everyProperty` and `someProperty` methods. + +
+people.everyProperty('isHappy', true) // false +people.someProperty('isHappy', true) // true +