Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EmberJS: How to load multiple models on the same route?

While I am not new to web development, I am quite new to to client-side MVC frameworks. I did some research and decided to give it a go with EmberJS. I went through the TodoMVC guide and it made sense to me...

I have setup a very basic app; index route, two models and one template. I have a server-side php script running that returns some db rows.

One thing that is very confusing me is how to load multiple models on the same route. I have read some information about using a setupController but I am still unclear. In my template I have two tables that I am trying to load with unrelated db rows. In a more traditional web app I would have just issued to sql statements and looped over them to fill the rows. I am having difficulty translating this concept to EmberJS.

How do I load multiple models of unrelated data on the same route?

I am using the latest Ember and Ember Data libs.

Update

although the first answer gives a method for handling it, the second answer explains when it's appropriate and the different methods for when it isn't appropriate.

like image 457
Eric Avatar asked Dec 11 '13 14:12

Eric


2 Answers

BEWARE:

You want to be careful about whether or not returning multiple models in your model hook is appropriate. Ask yourself this simple question:

  1. Does my route load dynamic data based on the url using a slug :id? i.e. this.resource('foo', {path: ':id'});

If you answered yes

Do not attempt to load multiple models from the model hook in that route!!! The reason lies in the way Ember handles linking to routes. If you provide a model when linking to that route ({{link-to 'foo' model}}, transitionTo('foo', model)) it will skip the model hook and use the supplied model. This is probably problematic since you expected multiple models, but only one model would be delivered. Here's an alternative:

Do it in setupController/afterModel

App.IndexRoute = Ember.Route.extend({
  model: function(params) {
    return $.getJSON('/books/' + params.id);
  },
  setupController: function(controller, model){
    this._super(controller,model);
    controller.set('model2', {bird:'is the word'});
  }
});

Example: http://emberjs.jsbin.com/cibujahuju/1/edit

If you need it to block the transition (like the model hook does) return a promise from the afterModel hook. You will need to manually keep track of the results from that hook and hook them up to your controller.

App.IndexRoute = Ember.Route.extend({
  model: function(params) {
    return $.getJSON('/books/' + params.id);
  },
  afterModel: function(){
    var self = this;
    return $.getJSON('/authors').then(function(result){
      self.set('authors', result);
    });
  }, 
  setupController: function(controller, model){
    this._super(controller,model);
    controller.set('authors', this.get('authors'));
  }
});

Example: http://emberjs.jsbin.com/diqotehomu/1/edit

If you answered no

Go ahead, let's return multiple models from the route's model hook:

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return {
           model1: ['red', 'yellow', 'blue'],
           model2: ['green', 'purple', 'white']
    };
  }
});

Example: http://emberjs.jsbin.com/tuvozuwa/1/edit

If it's something that needs to be waited on (such as a call to the server, some sort of promise)

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return Ember.RSVP.hash({
           model1: promise1,
           model2: promise2
    });
  }
});

Example: http://emberjs.jsbin.com/xucepamezu/1/edit

In the case of Ember Data

App.IndexRoute = Ember.Route.extend({
  var store = this.store;
  model: function() {
    return Ember.RSVP.hash({
           cats: store.find('cat'),
           dogs: store.find('dog')
    });
  }
});

Example: http://emberjs.jsbin.com/pekohijaku/1/edit

If one is a promise, and the other isn't, it's all good, RSVP will gladly just use that value

App.IndexRoute = Ember.Route.extend({
  var store = this.store;
  model: function() {
    return Ember.RSVP.hash({
           cats: store.find('cat'),
           dogs: ['pluto', 'mickey']
    });
  }
});

Example: http://emberjs.jsbin.com/coxexubuwi/1/edit

Mix and match and have fun!

App.IndexRoute = Ember.Route.extend({
  var store = this.store;
  model: function() {
    return Ember.RSVP.hash({
           cats: store.find('cat'),
           dogs: Ember.RSVP.Promise.cast(['pluto', 'mickey']),
           weather: $.getJSON('weather')
    });
  }, 
  setupController: function(controller, model){
    this._super(controller, model);
    controller.set('favoritePuppy', model.dogs[0]);
  }
});

Example: http://emberjs.jsbin.com/joraruxuca/1/edit

like image 149
Kingpin2k Avatar answered Oct 19 '22 05:10

Kingpin2k


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

like image 95
Marcio Junior Avatar answered Oct 19 '22 06:10

Marcio Junior