Controllers are dead; Long live Controllers

There has been a concern circulating the Ember.js community about the removal of controllers. In this post I will attempt to demystify what is happening and what you need to do to be future-proof.

Deprecations #

Proxying #

It is true that the proxying behaviour of controllers (ObjectController and ArrayController) is deprecated. In retrospect this behaviour created more confusion than it was helpful. In a proxying controller if you had {{foo}} in your template, it would first look up a foo property in the controller, and if the controller did not have a foo property, it would then look up foo in the model. ArrayController also had a special feature called item controllers which can be implemented with components instead.

This means that when using Ember.Controller, which does not have the proxying behaviour, {{foo}} is equivalent to {{}}. If you want the model’s foo property you need to explicitly do {{}}.

Long-lived state #

It is also true that coupling long-lived state with Routable Controllers (explained next) was a poor idea in retrospect. It was thought to be a good match because Routable Controllers are singletons, which means you only have one instance alive at the same time. This is why many people have to reset controller properties for forms.

This means that any long-lived state you have should be moved off to services where possible. A particular exception is query params, which are expected to move back to the route, as it was in the very first implementation of the concept.

Controller the concept #

To try to make this clearer, let me try to establish some terminology. You have Controller the concept, and then you have Ember.Controller the implementation. What I’m trying to explain is that an Ember.js application will still set up something that decorates the model, but instead of that being an Ember.Controller it will be a Ember.Component.

What’s a Controller? According to the guides, controllers allow you to decorate your models with display logic. More relevant to this discussion are Routable Controllers, which are controllers that are associated with a Route. For example, here we have an index Route:

// app/router.js{function() {
// app/routes/index.js
export default Ember.Route.extend({
  model() {
    return { colors: ['red', 'green', 'blue'] };

And the respective Routable Controller:

// app/controllers/index.js
export default Ember.Controller.extend({});

What will happen in the future is that Ember.js is changing the Routable Controllers implementation to the Routable Components implementation. A Routable Component still has the same duties as a controller but uses a different implementation with different semantics, which are explained next.

So, for the same index route as above, we’ll have:

// app/components/index.js (PATH FOR DEMO PURPOSES ONLY)
export default Ember.Component.extend({});

Semantic Future #

There are however some semantic changes:

Being future-proof #

Being future-proof does not mean never using any controller ever. I’ll try to be a little bit more useful and list some tasks you can do right now:


Now read this

Model inheritance for Ember.js routes

Update 2014-04-01 As of Ember.js 1.5.0 this feature is enabled by default for release builds. Update 2014-02-10 As of commit b351b4a7c41dbaa5d4b1c5508f02df6b957018c7 this feature is enabled by default. This includes Canary builds. I’ve... Continue →