EmberJS: Как загрузить несколько моделей по одному и тому же маршруту?

Хотя я не новичок в веб-разработке, я совершенно новичок в клиентских MVC-инфраструктурах. Я сделал некоторые исследования и решил дать ему поединок с EmberJS. Я прошел через руководство TodoMVC, и это имело смысл для меня...

У меня установлено очень простое приложение; индексный маршрут, две модели и один шаблон. У меня есть сервер php script, который возвращает некоторые строки db.

Одна вещь, которая меня очень смущает, - это загрузить несколько моделей по одному и тому же маршруту. Я прочитал некоторую информацию об использовании setupController, но я все еще неясен. В моем шаблоне у меня есть две таблицы, которые я пытаюсь загрузить с несвязанными строками db. В более традиционном веб-приложении я бы только что выпустил инструкции sql и зациклился над ними, чтобы заполнить строки. Мне трудно перевести эту концепцию на EmberJS.

Как загрузить несколько моделей несвязанных данных по одному и тому же маршруту?

Я использую последние библиотеки Ember и Ember Data.

Update

хотя первый ответ дает метод его обработки, второй ответ объясняет, когда он подходит, и разные методы, когда это не подходит.

Ответ 1

Вы можете использовать Ember.RSVP.hash для загрузки нескольких моделей:

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

В шаблоне вы можете обратиться к people и companies, чтобы получить загруженные данные:

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>

Это Twiddle с этим образцом: https://ember-twiddle.com/c88ce3440ab6201b8d58

Ответ 2

BEWARE:

Вы хотите быть осторожным относительно того, подходит ли возврат нескольких моделей в ваш крючок модели. Задайте себе этот простой вопрос:

  • Мой маршрут загружает динамические данные на основе URL-адреса с помощью slug :id? то есть this.resource('foo', {path: ':id'});

Если вы ответили да

Не пытайтесь загружать несколько моделей из крюка модели в этом маршруте!!! Причина заключается в том, как Ember обрабатывает ссылки на маршруты. Если вы указываете модель при привязке к этому маршруту ({{link-to 'foo' model}}, transitionTo('foo', model)), он пропустит крючок модели и использует поставляемую модель. Это, вероятно, проблематично, поскольку вы ожидали нескольких моделей, но только одна модель будет доставлена. Вот альтернатива:

Сделайте это в 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'});
  }
});

Пример: http://emberjs.jsbin.com/cibujahuju/1/edit

Если вам нужно заблокировать переход (например, крючок модели), верните обещание с помощью afterModel hook. Вам нужно будет вручную отслеживать результаты этого крючка и подключать их к контроллеру.

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

Пример: http://emberjs.jsbin.com/diqotehomu/1/edit

Если вы ответили no

Вперед, верните несколько моделей из крюка модели маршрута:

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

Пример: http://emberjs.jsbin.com/tuvozuwa/1/edit

Если это то, что нужно ждать (например, вызов сервера, какое-то обещание)

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

Пример: http://emberjs.jsbin.com/xucepamezu/1/edit

В случае Ember Data​​p >

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

Пример: http://emberjs.jsbin.com/pekohijaku/1/edit

Если это обещание, а другое - нет, все хорошо, RSVP с радостью будет использовать это значение

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

Пример: http://emberjs.jsbin.com/coxexubuwi/1/edit

Смешайте и поиграйте и получите удовольствие!

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

Пример: http://emberjs.jsbin.com/joraruxuca/1/edit

Ответ 3

Я использую что-то вроде ответа, предоставленного Марсио, но выглядит примерно так:

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

Ответ 4

Если вы используете данные Ember Data, для несвязанных моделей это становится еще проще:

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

Если вы хотите получить свойство объекта для model2, используйте DS.PromiseObject вместо 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'))
    });
  }
});

Ответ 5

Последняя версия JSON-API, реализованная в Ember Data v1.13, очень хорошо поддерживает объединение разных ресурсов в один и тот же запрос, если вы не возражаете изменить свои конечные точки API.

В моем случае у меня есть конечная точка session. Сессия относится к записи пользователя, а запись пользователя относится к различным моделям, которые я всегда хочу загружать в любое время. Это очень приятно, потому что все это входит в один запрос.

Одна оговорка в спецификации заключается в том, что все возвращаемые вами объекты должны быть связаны каким-либо образом с первичным объектом, который был получен. Я считаю, что данные ember-данных будут только пересекать явные отношения при нормализации JSON.

В других случаях я теперь предпочитаю откладывать загрузку дополнительных моделей до тех пор, пока страница не будет загружена, т.е. для отдельных панелей данных или чего-либо еще, поэтому, по крайней мере, страница отображается как можно быстрее. При этом происходит некоторая потеря/изменение с учетом состояния автоматической загрузки ошибок.