EmberJS:如何在同一条路线上加载多个模型?

IT技术 javascript ember.js rsvp.js
2021-01-12 17:08:08

虽然我对 Web 开发并不陌生,但我对客户端 MVC 框架还是很陌生。我做了一些研究,并决定尝试使用 EmberJS。我浏览了 TodoMVC 指南,这对我来说很有意义......

我已经设置了一个非常基本的应用程序;索引路由,两个模型和一个模板。我有一个服务器端 php 脚本正在运行,它返回一些 db 行。

让我非常困惑的一件事是如何在同一条路线上加载多个模型。我已经阅读了一些关于使用 setupController 的信息,但我仍然不清楚。在我的模板中,我有两个表,我试图用不相关的数据库行加载它们。在更传统的 Web 应用程序中,我只会向 sql 语句发出并循环它们以填充行。我很难将这个概念翻译成 EmberJS。

如何在同一路线上加载多个不相关数据模型?

我正在使用最新的 Ember 和 Ember Data 库。

更新

尽管第一个答案给出了处理它的方法,但第二个答案解释了何时合适以及何时不合适的不同方法。

6个回答

谨防:

您需要注意在模型挂钩中返回多个模型是否合适。问自己这个简单的问题:

  1. 我的路由是否使用 slug 加载基于 url 的动态数据:idIE 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钩子返回一个Promise您需要手动跟踪来自该挂钩的结果并将它们连接到您的控制器。

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

如果它需要等待(例如调用服务器,某种Promise)

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

如果一个是Promise,另一个不是,都很好,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/jorauxuca/1/edit

您仍然可以拥有多个模型而不会破坏链接。如果您有两个动态段,请传入一个 id(或字符串,如果您通过serialize:路由中钩子基于 slug 构建模型),而不是在链接中传入模型。仅供参考,访问模板中模型属性的语法是model.model1.somePropertymodel.puppyModel.someOtherProperty
2021-03-15 17:08:08
你当然可以,上面的大多数查询都不是动态段,而且大多数时候你不想为了正确设置链接而从不同的路由获取一堆不同的模型(一次再次适用于非动态模型)。
2021-03-19 17:08:08
因此,如果我的模型基于查询参数而不是动态段发生变化,我的答案是是还是否?
2021-03-22 17:08:08
以及如何将查询参数传递给 beforeModel、afterModel 和 setController 挂钩?
2021-03-27 17:08:08

注意:对于 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);
  }
});

在您的模板中,您可以参考peoplecompanies获取加载的数据:

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

如果您的路线中没有任何动态路段,这种方法就很好。如果您有动态段并且路径是通过输入的,{{link-to 'index' someModel}}那么模型挂钩将被完全跳过,这将打破这个例子。更强大的方法是加载任何setupController始终运行的额外模型
2021-03-18 17:08:08
@EoinKelly 我实际上会在这里使用 controller#afterModel 事件,因为您可以从 afterModel 返回一个Promise,并期望它的行为类似于模型,除了 afterModel 不会被跳过。
2021-03-29 17:08:08
@Eoin Kelly:您可以通过传递 ID(或 slug)而不是模型来绕过它
2021-04-01 17:08:08
这正是我要找的!
2021-04-07 17:08:08
您如何访问 setupcontroller 中的动态段、参数或查询参数 feom?
2021-04-10 17:08:08

我使用类似于 Marcio 提供的答案,但它看起来像这样:

    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 Data 方式,忽略我的第一条评论
2021-03-17 17:08:08
这会从 Ember.RSVP 返回一系列已解决的Promise,为什么不是 ember 方式?也许不是ember数据方式,但我没有使用ember数据。您可以在此处查看文档:emberjs.com/api/classes/RSVP.Promise.html
2021-03-26 17:08:08
这看起来不像 Ember 的方式
2021-03-28 17:08:08
这不会按预期工作。then函数中的逻辑没有任何区别。您正在弄乱异步代码并返回。
2021-04-02 17:08:08

接受接受的答案,并为 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>

如果您使用 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'))
    });
  }
});