EmberJS: How to load multiple models on the same route?
NOTE: for Ember 3.16+ apps, here is the same code, but with updated syntax / patterns: https://stackoverflow.com/a/62500918/356849
The below is for Ember < 3.16, even though the code would work as 3.16+ as fully backwards compatible, but it's not always fun to write older code.
You can use the Ember.RSVP.hash to load several models:
app/routes/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return Ember.RSVP.hash({
people: this.store.findAll('person'),
companies: this.store.findAll('company')
});
},
setupController(controller, model) {
this._super(...arguments);
Ember.set(controller, 'people', model.people);
Ember.set(controller, 'companies', model.companies);
}
});
And in your template you can refer to people
and companies
to get the loaded data:
app/templates/index.js
<h2>People:</h2>
<ul>
{{#each people as |person|}}
<li>{{person.name}}</li>
{{/each}}
</ul>
<h2>Companies:</h2>
<ul>
{{#each companies as |company|}}
<li>{{company.name}}</li>
{{/each}}
</ul>
This is a Twiddle with this sample: https://ember-twiddle.com/c88ce3440ab6201b8d58
How to use multiple models with a single route in EmberJS / Ember Data?
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)
How to load multiple models sequentially in Ember JS route
You can create your own promise, and resolve a hash. That will be the model in the controller, no need to override setupController
unless you want them stored on the controller somewhere different.
App.ProductRoute = Ember.Route.extend({
model: function(params) {
var self = this;
return new Em.RSVP.Promise(function(resolve, reject){
// first call
self.store.find('Product').then(function(products){
// second call should only be called after the first find is completed
self.store.find('Product', params.product_id).then(function(product){
// third call should only be called after the above is completed
self.store.find('ProductOptions', product.get('optionId').then(function(productOption){
resolve({
products:products,
product:product,
productOptions:productOptions
});
});
});
});
});
}
});
BTW, it sounds like product options should be an async relationship on the product.
EmberJS How to load multiple models on the same route inside a single sorted array
I recommend using a Computed.property
which monitors the models that are changing and combines them into the array you need in your template
.
When modelA
or modelB
changes your computed property will update with those results.
myList: Ember.computed('modelA', 'modelB', function() {
let combinedModels = [];
this.get('modelA').forEach( item => {
// pull out what you need from each model item
combinedModels.push(item);
});
this.get('modelB').forEach( item => {
// pull out what you need from each model item
combinedModels.push(item);
});
return combinedModels;
});
Load multiple model data in same api call emberjs?
a few things to notice:
- dont put the
id
inattributes
- dont name an attribute
type
. Really dont! It's a reserved keyword. - relationships are not attributes and should be under
relationships
- use the
included
array to sideload data - ids must be strings
so for example this would be a valid payload:
{
"meta": {
"type": "match"
},
"data": [
{
"id": "1119536",
"type": "team",
"attributes": {
"match-type": "match"
},
"relationships": {
"team": {
"data": {
"type": "team",
"id": "1"
}
},
"opponent": {
"data": {
"type": "team",
"id": "3"
}
}
}
}
],
"included": [
{
"type": "team",
"id": "1",
"attributes": {
"name": "England",
"logo": null
}
},
{
"type": "team",
"id": "3",
"attributes": {
"name": "Pakistan",
"logo": null
}
}
]
}
EmberJS - Printing model properties when multiple models on a route
1., don't forget to call this._super(controller, models);
on the first line of setupController(controller, models)
2., in your template use some of these {{model.sginp.firstObject.name}} {{sginp.firstObject.name}} {{model.weekdayplan.firstObject.day}} {{weekdayplan.firstObject.day}}
Proper way to set multiple models on route; depending on user authentication?
There is a set of data that you always want to load, for every user. Do that in the model
hook, that is actually the data for the route.
There is another piece of info that you want to add only if a condition is met (authentication). Do that in the afterModel
hook.
...is provided the route's resolved model...
http://emberjs.com/api/classes/Ember.Route.html#method_afterModel
So, now you can append or remove data from the model. Or take any relevant action depending on the data that you received.
Related Topics
Are There Constants in JavaScript
Detecting Arrow Key Presses in JavaScript
Render Partial View Using Jquery in ASP.NET MVC
Where Do You Include the Jquery Library From? Google JSAPI? CDN
Define Global Variable with Webpack
How to Iterate Through Table Rows and Cells in JavaScript
How to Getelementbyclass Instead of Getelementbyid with JavaScript
How to Sort Array Inside Collection Record in Mongodb
Event Handlers Inside a JavaScript Loop - Need a Closure
What Do Square Brackets Around a Property Name in an Object Literal Mean
Is There a Better Way to Do Optional Function Parameters in JavaScript
Why Doesn't a JavaScript Return Statement Work When the Return Value Is on a New Line
What Do These JavaScript Bitwise Operators Do
Youtube Iframe API: How to Control an Iframe Player That's Already in the HTML
Two Sets of Parentheses After Function Call