From reading the docs, it looks like you have to (or should) assign a model to a route like so:
App.PostRoute = Ember.Route.extend({ model: function() { return App.Post.find(); } });
What if I need to use several objects in a certain route? i.e. Posts, Comments and Users? How do I tell the route to load those?
Last update forever: I can't keep updating this. So this is deprecated and will likely be this way. here's a better, and more up-to-date thread EmberJS: How to load multiple models on the same route?
Update: In my original answer I said to use embedded: true
in the model definition. That's incorrect. In revision 12, Ember-Data expects foreign keys to be defined with a suffix (link) _id
for single record or _ids
for collection. Something similar to the following:
{ id: 1, title: 'string', body: 'string string string string...', author_id: 1, comment_ids: [1, 2, 3, 6], tag_ids: [3,4] }
I have updated the fiddle and will do so again if anything changes or if I find more issues with the code provided in this answer.
Answer with related models:
For the scenario you are describing, I would rely on associations between models (setting and only load the embedded: true
)Post
model in that route, considering I can define a DS.hasMany
association for the Comment
model and DS.belongsTo
association for the User
in both the Comment
and Post
models. Something like this:
App.User = DS.Model.extend({ firstName: DS.attr('string'), lastName: DS.attr('string'), email: DS.attr('string'), posts: DS.hasMany('App.Post'), comments: DS.hasMany('App.Comment') }); App.Post = DS.Model.extend({ title: DS.attr('string'), body: DS.attr('string'), author: DS.belongsTo('App.User'), comments: DS.hasMany('App.Comment') }); App.Comment = DS.Model.extend({ body: DS.attr('string'), post: DS.belongsTo('App.Post'), author: DS.belongsTo('App.User') });
This definition would produce something like the following:
With this definition, whenever I find
a Post, I will have access to a collection of comments associated with that post, and the comment's author as well, and the user which is the author of the post, since they are all embedded. The route stays simple:
App.PostsPostRoute = Em.Route.extend({ model: function(params) { return App.Post.find(params.post_id); } });
So in the PostRoute
(or PostsPostRoute
if you're using resource
), my templates will have access to the controller's content
, which is the Post
model, so I can refer to the author, simply as author
<script type="text/x-handlebars" data-template-name="posts/post"> <h3>{{title}}</h3> <div>by {{author.fullName}}</div><hr /> <div> {{body}} </div> {{partial comments}} </script> <script type="text/x-handlebars" data-template-name="_comments"> <h5>Comments</h5> {{#each content.comments}} <hr /> <span> {{this.body}}<br /> <small>by {{this.author.fullName}}</small> </span> {{/each}} </script>
(see fiddle)
Answer with non-related models:
However, if your scenario is a little more complex than what you described, and/or have to use (or query) different models for a particular route, I would recommend to do it in Route#setupController
. For example:
App.PostsPostRoute = Em.Route.extend({ model: function(params) { return App.Post.find(params.post_id); }, // in this sample, "model" is an instance of "Post" // coming from the model hook above setupController: function(controller, model) { controller.set('content', model); // the "user_id" parameter can come from a global variable for example // or you can implement in another way. This is generally where you // setup your controller properties and models, or even other models // that can be used in your route's template controller.set('user', App.User.find(window.user_id)); } });
And now when I'm in the Post route, my templates will have access to the user
property in the controller as it was set up in setupController
hook:
<script type="text/x-handlebars" data-template-name="posts/post"> <h3>{{title}}</h3> <div>by {{controller.user.fullName}}</div><hr /> <div> {{body}} </div> {{partial comments}} </script> <script type="text/x-handlebars" data-template-name="_comments"> <h5>Comments</h5> {{#each content.comments}} <hr /> <span> {{this.body}}<br /> <small>by {{this.author.fullName}}</small> </span> {{/each}} </script>
(see fiddle)
Using Em.Object
to encapsulate multiple models is a good way to get all data in model
hook. But it can't ensure all data is prepared after view rendering.
Another choice is to use Em.RSVP.hash
. It combines several promises together and return a new promise. The new promise if resolved after all the promises are resolved. And setupController
is not called until the promise is resolved or rejected.
App.PostRoute = Em.Route.extend({ model: function(params) { return Em.RSVP.hash({ post: // promise to get post comments: // promise to get comments, user: // promise to get user }); }, setupController: function(controller, model) { // You can use model.post to get post, etc // Since the model is a plain object you can just use setProperties controller.setProperties(model); } });
In this way you get all models before view rendering. And using Em.Object
doesn't have this advantage.
Another advantage is you can combine promise and non-promise. Like this:
Em.RSVP.hash({ post: // non-promise object user: // promise object });
Check this to learn more about Em.RSVP
: https://github.com/tildeio/rsvp.js
But don't use Em.Object
or Em.RSVP
solution if your route has dynamic segments
The main problem is link-to
. If you change url by click link generated by link-to
with models, the model is passed directly to that route. In this case the model
hook is not called and in setupController
you get the model link-to
give you.
An example is like this:
The route code:
App.Router.map(function() { this.route('/post/:post_id'); }); App.PostRoute = Em.Route.extend({ model: function(params) { return Em.RSVP.hash({ post: App.Post.find(params.post_id), user: // use whatever to get user object }); }, setupController: function(controller, model) { // Guess what the model is in this case? console.log(model); } });
And link-to
code, the post is a model:
{{#link-to "post" post}}Some post{{/link-to}}
Things become interesting here. When you use url /post/1
to visit the page, the model
hook is called, and setupController
gets the plain object when promise resolved.
But if you visit the page by click link-to
link, it passes post
model to PostRoute
and the route will ignore model
hook. In this case setupController
will get the post
model, of course you can not get user.
So make sure you don't use them in routes with dynamic segments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With