EmberJS: Como carregar vários modelos na mesma rota?

Embora eu não seja novo no desenvolvimento da web, eu sou muito novo nos frameworks MVC do lado do cliente. Eu fiz algumas pesquisas e decidi dar uma chance ao EmberJS. Eu passei pelo guia TodoMVC e fez sentido para mim …

Eu configurei um aplicativo muito básico; rota de índice, dois modelos e um modelo. Eu tenho um script php do lado do servidor em execução que retorna algumas linhas de db.

Uma coisa que está me confundindo é como carregar vários modelos na mesma rota. Eu li algumas informações sobre o uso de um setupController, mas ainda não estou claro. No meu modelo, tenho duas tabelas que estou tentando carregar com linhas de db não relacionadas. Em um aplicativo da web mais tradicional, eu teria apenas emitido para instruções sql e colocado em loop sobre elas para preencher as linhas. Estou tendo dificuldade em traduzir este conceito para o EmberJS.

Como faço para carregar vários modelos de dados não relacionados na mesma rota?

Eu estou usando as últimas bibliotecas Ember e Ember Data.

Atualizar

embora a primeira resposta forneça um método para lidar com ela, a segunda resposta explica quando é apropriado e os diferentes methods para quando não é apropriado.

   

Você pode usar o Ember.RSVP.hash para carregar vários modelos:

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); } }); 

E no seu modelo você pode se referir a people e companies para obter os dados carregados:

app/templates/index.js

 

People:

    {{#each people as |person|}}
  • {{person.name}}
  • {{/each}}

Companies:

    {{#each companies as |company|}}
  • {{company.name}}
  • {{/each}}

Este é um Twiddle com este exemplo: https://ember-twiddle.com/c88ce3440ab6201b8d58

CUIDADO:

Você quer ser cuidadoso sobre a devolução ou não de vários modelos em seu modelo de gancho. Pergunte a si mesmo esta pergunta simples:

  1. Minha rota carrega dados dynamics com base na URL usando um slug :id ? isto é, this.resource('foo', {path: ':id'});

Se você respondeu sim

Não tente carregar vários modelos do gancho do modelo nessa rota !!! A razão está no modo como a Ember lida com as rotas. Se você fornecer um modelo ao vincular a essa rota ( {{link-to 'foo' model}} , transitionTo('foo', model) ), ele ignorará o modelo hook e usará o modelo fornecido. Isso provavelmente é problemático, já que você esperava vários modelos, mas apenas um modelo seria entregue. Aqui está uma alternativa:

Faça isso em 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'}); } }); 

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

Se você precisar bloquear a transição (como faz o modelo hook), retorne uma promise do hook afterModel . Você precisará controlar manualmente os resultados desse gancho e conectá-los ao seu controlador.

 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')); } }); 

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

Se você respondeu não

Vá em frente, vamos retornar vários modelos do gancho de modelo da rota:

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

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

Se é algo que precisa ser esperado (como uma chamada para o servidor, algum tipo de promise)

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

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

No caso de dados Ember

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

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

Se uma é uma promise e a outra não, tudo bem, RSVP terá todo o prazer em usar esse valor

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

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

Misture e combine e divirta-se!

 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]); } }); 

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

Eu uso algo como a resposta que Marcio forneceu, mas parece algo assim:

  var products = Ember.$.ajax({ url: api + 'companies/' + id +'/products', dataType: 'jsonp', type: 'POST' }).then(function(data) { return data; }); var clients = Ember.$.ajax({ url: api + 'clients', dataType: 'jsonp', type: 'POST' }).then(function(data) { return data; }); var updates = Ember.$.ajax({ url: api + 'companies/' + id + '/updates', dataType: 'jsonp', type: 'POST' }).then(function(data) { return data; }); var promises = { products: products, clients: clients, updates: updates }; return Ember.RSVP.hash(promises).then(function(data) { return data; }); 

Se você usa Ember Data, fica ainda mais simples para modelos não relacionados:

 import Ember from 'ember'; import DS from 'ember-data'; export default Ember.Route.extend({ setupController: function(controller, model) { this._super(controller,model); var model2 = DS.PromiseArray.create({ promise: this.store.find('model2') }); model2.then(function() { controller.set('model2', model2) }); } }); 

Se você deseja apenas recuperar a propriedade de um object para model2 , use DS.PromiseObject em vez de DS.PromiseArray :

 import Ember from 'ember'; import DS from 'ember-data'; export default Ember.Route.extend({ setupController: function(controller, model) { this._super(controller,model); var model2 = DS.PromiseObject.create({ promise: this.store.find('model2') }); model2.then(function() { controller.set('model2', model2.get('value')) }); } }); 

A versão mais recente da JSON-API, conforme implementada no Ember Data v1.13, suporta o agrupamento de diferentes resources na mesma solicitação muito bem, se você não se importar em modificar seus terminais da API.

No meu caso, eu tenho um endpoint de session . A session está relacionada a um registro do usuário e o registro do usuário está relacionado a vários modelos que sempre quero carregar em todos os momentos. É muito bom para tudo entrar com o único pedido.

Uma ressalva de acordo com a especificação é que todas as entidades que você retorna devem estar ligadas de alguma forma à entidade principal que está sendo recebida. Acredito que os dados de ember só irão percorrer os relacionamentos explícitos ao normalizar o JSON.

Para outros casos, estou agora optando por adiar o carregamento de modelos adicionais até que a página já esteja carregada, ou seja, para painéis separados de dados ou qualquer outra coisa, portanto, pelo menos, a página é renderizada o mais rápido possível. Fazendo isso, há alguma perda / alteração com o estado “automático” de carregamento de erro a ser considerado.