Multiple models on one page? Is it really that difficult?


#1

I am relatively new with Ember, written a few different web pages with it. Still haven’t made my mind up whether I like it or not as most things have been a faff on. But I’m hoping it will get clearer with time and hope I gradually get things right the first time more.

If I am using ember data, and I have a page with 2+ completely unrelated lists on say users, and products, how is the right way to handle this? On the surface it seems a really obvious thing to want to do. If I use RSVP.hash I have a feeling that refreshing one list (for paginating it) will refresh the other one as well but this is not efficient. But I want to paginate one of the lists, why would I want the other list to be retrieved as well? Can’t I keep them separate?

Why is Ember making what would normally be a simple thing an architectural problem?


#2

You could keep the logic in a service which you can access from your route. This way you could keep the logic for when each list should be refreshed in the service, and you could inject this service into your route and access these methods.


#3

Hi Ben.

You’re probably using RSVP.hash() in your route.model() function. But the result of the promise then gets copied to some controller properties, right? I think you can work with those properties independently in the controller (and in the router as well) - just use this.store to do whatever you need and update the corresponding model with this.set('prop', result)


#4

A quick answer became a very long post, hope it comes up being useful.

tl;dr: Ember’s store reuses data if it’s already in it, don’t worry too much about using RSVP.hash(). Sometimes you might want to use this.store.find() somewhere else that isn’t a route. And finally, look at the ApplicationRoute and outlets.


It really shouldn’t be a problem for you to use RSVP.hash();. Since you are using Ember Data, once you have the data in the store, it will use it and won’t try to call the server again unless you are using queries.

What I mean for this is:

return this.store.find('user', 1)

Will make an AJAX request the first time you call it. Then, if you use it again in the app by either coming back to the route or calling it inside some other route/controller, it won’t make an AJAX request, it will know that it’s in your Ember store and continue right away.

Don’t worry too much about using RSVP.hash().


Pagination on the other hand usually requires to use a query like this:

return this.store.find('user', { offset: 100, limit: 10 })

Those always make an AJAX request, and that’s probably what you want most of the time you want to paginate. If you don’t and have all your data in the store already; you can use all.

var users = this.store.all('user');
return users.slice(100, 110);

all is synchronous and returns an array like object, in which you can use all the array functions. http://emberjs.com/api/data/classes/DS.Store.html#method_all


Also, if you feel like you don’t need to load all your data at once in the route and want to load the page right away and then show individual loading icons for things in your page. It’s probably worth looking at setupController() or run your promises inside the controller / component.

setupController: function(controller, model) {
  this._super.apply(this, arguments);
  this.store.find('user', 1).then(function(u) {
    controller.set('user', u);
  });
  this.store.find('posts', { offset: 100, limit: 10 }).then(function(p) {
    controller.set('posts', p);
  });
}

That will load your template immediately in which you can show a loading icon if user or posts are empty. Then, display the data once they are available.


Once more, sometimes you don’t need to use a route or don’t want a URL to paginate your data; then can call this.store.find(); inside your controller. That’s perfectly fine, or even inside a component. I usually prefer to use a route to make all AJAX requests, but there will be ocassions when you will be forced to do it in a controller or component and that’s ok.

One more thing, if this is data that have nothing to do with the route itself, like header information or a persistent sidebar in your app. You probably want to handle those in your ApplicationRoute instead and use outlet to render this data.

http://guides.emberjs.com/v1.12.0/routing/rendering-a-template/


#5

Thanks for the long reply. Though, it was only the RSVP that was relevant. But thanks for the extra general information. However, I probably will not use controllers as they are going to be leaned away from…


#6

You can also make a “super model” that aggregates all of the models for a page for you. This is especially useful if you have things like “image” or “media” models that get used several times on a page. For example:

var MultipleChoiceQuizPage = Page.extend({
    header: DS.attr("string"),
    promptAudio: DS.belongsTo("media", {async: true}),
    answerAudio: DS.belongsTo("media", {async: true}),
    answerOptions: DS.hasMany("multipleChoiceQuizChoice", {async: true})
});

In your persistence layer, store a record for each combination of elements that make up a page. Then your model hook becomes something like:

model: function(parameters){
    return this.store.find("multipleChoiceQuizPage", parameters.id);
}

In a lot of cases, I think this is simpler than using RSVP.hash in your model hook, making the store request directly from the component, or using a ton of service calls.