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][guides-controllers]. More relevant to this discussion are Routable Controller
s, 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:
- Like mentioned previously,
Routable Components
will not be singletons. - The
Route
will now set theRoutable Component
‘sattrs
hook instead of themodel
hook. This comes from the separation between attributes (passed in) and properties (internal) that happens with “angle bracket components”. - Proxying controllers (
ObjectController
andArrayController
) will be removed in2.0
. BothRoutable Controllers
(Ember.Controller
) andRoutable Components
(Ember.Component
) do not proxy.
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:
- Substitute
{{render}}
,{{partial}}
and{{view}}
calls for components. ({{render}}
specifically creates a controller, which we want to avoid because a component is a superior abstraction) - Do not use proxying controllers (
ObjectController
/ArrayController
). - Generate controllers for all your routes. Since Ember 1.x dynamically generates
ObjectController
/ArrayController
for you for legacy reasons, you don’t want to accidentally depend on them. note: if you have [ember-disable-proxy-controllers][ember-disable-proxy-controllers] in yourember-cli
project, onlyEmber.Controller
will be generated. - Replace item controllers with components. (Upcoming blog post)
Routable Components
are not singletons, so long-lived state should be moved to services.
[itemcontrollers]: https://svbtle.com/controllers-are-dead-long-life-controllers
[ember-disable-proxy-controllers]: https://github.com/cibernox/ember-disable-proxy-controllers
[guides-controllers]: http://guides.emberjs.com/v1.12.0/controllers/