EmberJS: Як завантажити кілька моделей за одним маршрутом?


75

Незважаючи на те, що я не новачок у веб-розробці, я абсолютно новачок у платформах MVC на стороні клієнта. Я провів кілька досліджень і вирішив спробувати його з EmberJS. Я пройшов посібник TodoMVC, і це для мене мало сенс ...

Я налаштував дуже простий додаток; індексний маршрут, дві моделі та один шаблон. У мене запущений серверний php-скрипт, який повертає деякі db-рядки.

Мене дуже бентежить, як завантажувати кілька моделей на одному маршруті. Я прочитав деяку інформацію про використання setupController, але мені все ще незрозуміло. У моєму шаблоні у мене є дві таблиці, які я намагаюся завантажити з не пов'язаними між собою рядками db. У більш традиційному веб-додатку я б просто видав оператори sql і зациклював їх, щоб заповнити рядки. У мене виникають труднощі з перекладом цього поняття на EmberJS.

Як завантажити кілька моделей не пов’язаних між собою даних на одному маршруті?

Я використовую найновіші бібліотеки Ember і Ember Data.

Оновлення

хоча перша відповідь дає спосіб обробки, друга відповідь пояснює, коли це доречно, і різні методи, коли це не доречно.


Це відповідає на ваше запитання? Запитуйте дві моделі разом
Панайотис Панагі,

Відповіді:


95

ПРИМІТКА: для програм Ember 3.16+ є той самий код, але з оновленим синтаксисом / шаблонами: https://stackoverflow.com/a/62500918/356849

Нижче наведено для Ember <3.16, хоча код працював би як 3.16+ як повністю зворотний, але писати старий код не завжди цікаво.


Ви можете використовувати 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


9
Цей підхід чудовий, якщо у вас немає динамічних сегментів у вашому маршруті. Якщо у вас є динамічні сегменти, і маршрут введено через, {{link-to 'index' someModel}}тоді гачок моделі повністю пропускається, що порушить цей приклад. Більш надійним підходом є завантаження будь-яких додаткових моделей, в setupControllerяких завжди працює.
Eoin Kelly

Це саме я шукав!
PDXIII

1
@EoinKelly я б насправді використовував тут подію контролера # afterModel, оскільки ви можете повернути обіцянку від afterModel і очікувати, що вона буде поводитися як модель, за винятком, що afterModel не буде пропущено.
Самсініте

1
Як отримати доступ до динамічного сегмента, параметрів або параметрів запиту feom у контролері налаштування?
ahnbizcad

1
@Eoin Kelly: ви можете обійти це, передавши ідентифікатор (або куля) замість моделі
ahnbizcad

151

ОСТОРОЖНО:

Ви хочете бути обережними щодо того, чи доцільно повертати кілька моделей у вашому гачку моделі. Задайте собі таке просте запитання:

  1. Чи завантажує мій маршрут динамічні дані на основі URL-адреси за допомогою кулі :id? тобто this.resource('foo', {path: ':id'});

Якщо ви відповіли ствердно

Не намагайтеся завантажувати кілька моделей із гачка моделі в цьому напрямку !!! Причина криється в тому, як Ембер обробляє посилання на маршрути. Якщо ви надаєте модель під час посилання на цей маршрут ( {{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гачка. Вам потрібно буде вручну відстежувати результати з цього гачка та підключати їх до свого контролера.

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

Якщо ви відповіли ні

Вперед, повернемо кілька моделей із гачка моделей маршруту:

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

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


Отже, якщо моя модель змінюється на основі параметрів запиту, а не динамічного сегмента, моя відповідь так чи ні?
ahnbizcad

і як я можу передавати параметри запиту до гачків beforeModel, afterModel та setController?
ahnbizcad

2
Ви все ще можете мати кілька моделей і не порушувати свої посилання. якщо у вас є два динамічні сегменти, передайте ідентифікатор (або рядок, якщо ви будуєте свою модель на основі кулі через serialize:гачок у вашому маршруті), а не передаєте модель за посиланням. FYI синтаксис доступу до властивостей моделі в шаблонах буде model.model1.somePropertyабоmodel.puppyModel.someOtherProperty
ahnbizcad

Ви впевнені, що більшість запитів, наведених вище, не є динамічними сегментами, і більшу частину часу з різних маршрутів ви не хочете отримувати купу різних моделей в іншому маршруті, щоб лише правильно налаштувати посилання (один раз знову застосовується до нединамічних моделей).
Kingpin2k

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

Це не схоже на Ембер
Адріан

Це повертає масив вирішених обіцянок від Ember.RSVP, чому це не так? можливо не так, як дані ember, але я не використовував дані ember. Документи ви можете переглянути тут: emberjs.com/api/classes/RSVP.Promise.html
la_antorcha

Правильно, я мав на увазі шлях Ember Data, проігноруйте мій перший коментар
Адріан

1
Це не працює належним чином. Логіка thenфункцій не має різниці. Ви псуєте асинхронний код і повертаєтеся.
Джелхан

2

Якщо ви використовуєте дані Ember, це стає ще простішим для непов’язаних моделей:

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

2

Остання версія JSON-API , реалізована в Ember Data v1.13, дуже добре підтримує об’єднання різних ресурсів в одному запиті, якщо ви не проти змінити кінцеві точки API.

У моєму випадку у мене є sessionкінцева точка. Сеанс стосується запису користувача, а запис користувача стосується різних моделей, які я завжди хочу завантажувати постійно. Дуже приємно, що все це можна прийти з одним запитом.

Одне застереження в специфікації полягає в тому, що всі сутності, які ви повертаєте, повинні бути якимось чином пов’язані з основною суттю, що отримується. Я вважаю, що дані ember перетинатимуть явні зв'язки лише при нормалізації JSON.

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


2

Беручи прийняту відповідь та оновлюючи її для Ember 3.16+

app/routes/index.js

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class IndexRoute extends Route {
  @service store;

  async model() {
    let [people, companies] = await Promise.all([
      this.store.findAll('person'),
      this.store.findAll('company'),
    ]);


    return { people, companies };
  }

}

Зауважте, рекомендується не використовувати setupController для встановлення псевдонімів, оскільки він затушовує, звідки надходять дані та як вони переходять від маршруту до шаблону.

Отже, у вашому шаблоні ви можете зробити:

<h2>People:</h2>

<ul>
  {{#each @model.people as |person|}}
    <li>{{person.name}}</li>
  {{/each}}
</ul>

<h2>Companies:</h2>

<ul>
  {{#each @model.companies as |company|}}
    <li>{{company.name}}</li>
  {{/each}}
</ul>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.