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 {{controller.foo}}. If you want the model’s foo property you need to explicitly do {{model.foo}}.

 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
Router.map({function() {
  this.route('index');
});
// 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:

 
390
Kudos
 
390
Kudos

Now read this

Nested components and angle brackets, a sneaky solution

If you have been following Ember development, you might have noticed that starting with Ember v3.4, you have a new way to invoke components in your templates called angle bracket syntax. In the new syntax, instead of invoking a component... Continue →