Since the v2 router came it became clear that using global singleton controllers like App.userController = App.UserController.create()
is not the way to go. This prevents us from doing a simple binding like
App.UserController = Ember.Controller.extend({
accountsBinding: "App.accountsController.content"
})
There is no need or even possibility to manage the controller instances with the new router though. It will create the instance for us. One way we can use this is with this.controllerFor
, which can be used inside of a route.
App.UserRoute = Ember.Route.extend({
setupController: function(controller, model) {
// some magic with `this.controllerFor("user")`
}
})
but since this method is only available on the route and not inside a controller, it wasn't very pleasant to specify dependencies (or needs) between controllers. Which is exactly where needs come in and solve the issue
App.UserController = Ember.Controller.extend({
needs: ["foo"]
});
this will give you the opportunity to call controllers.foo
on the App.UserController
instance and get back an instance of App.FooController
. You could even (ab)use that in the templates like this
<!-- inside `users` template -->
{{controllers.foo}}
Needs become incredibly useful when you have nested routes, for example
App.Router.map(function() {
this.resource("post", { path: "/posts/:post_id" }, function() {
this.route("edit", { path: "/edit" });
});
});
In this case we will get post
, post.index
and post.edit
. If you go to /posts/1
you expect to get post.index
template, which is true, but the context (or model, or content) is being set on the PostController
, not on PostIndexController
.
When you think about it it does make sense, because the resource
is basically shared between post.index
and post.edit
, that's why it is fetched and stored in their parent. Let's go through this in detail:
- visit
/posts/1
- router basically does
App.Post.find(1)
and assigns that to the content ofPostController
- template
post
is rendered - template
post.index
is rendered inpost
's outlet
and when you transition to /posts/1/edit
, the only thing that changes is the leaf route, you still keep the same App.Post
model, because it belongs to the parent PostRoute
, not to the leaf PostIndexRoute
. But this has a drawback. You're not able to directly access the content from the post.index
template, since it doesn't belong to it's controller. That's where needs come in.
App.PostIndexController = Ember.Controller.extend({
needs: ["post"]
})
and in the post/index
template, you can access the content like this
{{controllers.post.content}}
By specifying the need Ember will make sure that it gives you the right PostController
instance with it's content set to the right value.
@twintubo if its common to bind against
controllers.post
, maybe needs should synthesis a top level post property directly on the host controller@darthdeus another thing to consider is typeInjections, the work similar to needs, but without the runtime dependency validation. And rather the the lazy
controllers.<otherController>
lookup, they link the controllers at creation.I am in the progress of some related work, trying to consolidate the ember di/container ideas, into some simple guides. So if u your anyone has ideas/concerns/questions ping me