Dom Christie

Array Controllers in Ember.js

An array controller is a wrapper for a collection of objects, and provides convenient methods for dealing with its contents.

An array controller’s model is typically set in a route, for example:

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return [
      {name: 'red'},
      {name: 'yellow'},
      {name: 'blue'}
    ];
  }
});
// (The Index ArrayController is setup implicitly)

Setting an array controller’s model sets up its content property, which forms the basis for other properties and methods.

arrangedContent

arrangedContent is an important property, defined as “the array that the [array controller] pretends to be”. It provides a way for sorted/filtered content to be stored separately from the original content. In this way, sorting/filtering is not destructive, and the content (in its original form) can still be retrieved.

By default, when an array controller has no sortProperties, arrangedContent and content are the same. When sortProperties are added, the sorted content is stored in arrangedContent whilst the original content remains untouched.

It’s important to note that an array controller should be treated just like an array, with its items referencing those in arrangedContent. Accessing items on an array controller itself, is effectively the same as accessing items in arrangedContent (an important difference is discussed in below).

This is best illustrated with some examples. Given the route above, the following #each loops all result in the same output:

index template:

{{#each}} {{name}} {{/each}}

{{#each content}} {{name}} {{/each}}

{{#each arrangedContent}} {{name}} {{/each}}

http://emberjs.jsbin.com/nuzoja/1

Setting sortProperties results in sorted output from arrangedProperty and therefore from the array controller instance itself:

App.IndexController = Ember.ArrayController.extend({
  sortProperties: ['name']
});

index template:

{{!-- Sorted by name --}}
{{#each}}

{{!-- Not Sorted --}}
{{#each content}}

{{!-- Sorted by name --}}
{{#each arrangedContent}}

Item Controllers

Adding an itemController property to an array controller wraps each item in an instance of the referenced controller. However, this only applies when accessing items through the array controller instance itself. Items accessed via arrangedContent or content remain unwrapped. This is the key difference when accessing items via arrangedContent versus accessing them via the array controller instance itself.

The following example demonstrates this concept:

App.IndexController = Ember.ArrayController.extend({
  itemController: 'color'
});

App.ColorController = Ember.ObjectController.extend({
  isActive: true
});

index template:

{{!-- Names rendered --}}
{{#each}}
  {{#if isActive}} {{name}} {{/if}}
{{/each}}

{{!-- Nothing rendered --}}
{{#each content}}
  {{#if isActive}} {{name}} {{/if}}
{{/each}}

{{!-- Nothing rendered --}}
{{#each arrangedContent}}
  {{#if isActive}} {{name}} {{/if}}
{{/each}}

http://emberjs.jsbin.com/nuzoja/3/edit

Nothing is rendered in the loops that iterate over content or arrangedContent because the items are not wrapped in an item controller and therefore isActive is inaccessible.

{{#each itemController='…'}}

{{#each}} helpers, when supplied with an itemController property, wrap each item in a new instance of the referenced controller. This operates entirely independently from the itemController property on an array controller: an array controller will not have access to any item controller created via an {{#each}} helper.

This becomes particularly important when implementing a computed property on an array controller that depends on properties on an item controller. See the example.

For more information: