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


75

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

我已经安装了一个非常基本的应用程序;索引路径,两个模型和一个模板。我有一个服务器端运行的php脚本,它返回一些数据库行。

让我非常困惑的一件事是如何在同一条路线上加载多个模型。我已经阅读了一些有关使用setupController的信息,但仍不清楚。在我的模板中,我有两个要与不相关的数据库行一起加载的表。在一个更传统的Web应用程序中,我将刚发布给sql语句并循环遍历它们以填充行。我很难将此概念转换为EmberJS。

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

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

更新资料

尽管第一个答案提供了处理该问题的方法,但第二个答案说明了什么时候适当以及什么时候不合适的不同方法。


这回答了你的问题了吗?一起请求两个模型
Panagiotis Panagi

Answers:


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

在您的模板中,您可以引用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>

这是一个带有该示例的小提琴:https ://ember-twiddle.com/c88ce3440ab6201b8d58


9
如果您的路线中没有任何动态细分,则此方法很好。如果您有动态线段,并且通过输入了路线{{link-to 'index' someModel}},则将完全跳过模型挂钩,这将破坏本示例。一种更可靠的方法是加载setupController始终在其中运行的任何其他模型。
Eoin Kelly 2014年

这正是我在寻找!
PDXIII 2014年

1
@EoinKelly我实际上在这里使用controller#afterModel事件,因为您可以从afterModel返回一个promise并期望它的行为类似于模型,但afterModel不会被跳过。
Samsinite 2015年

1
您如何在setupcontroller中访问动态段,参数或查询参数?
ahnbizcad 2015年

1
@Eoin Kelly:您可以通过传递ID(或
子弹

151

谨防:

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

  1. 我的路由是否会使用slug根据url加载动态数据: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

如果您需要它来阻止过渡(如模型钩子一样),则从钩子中返回一个Promise 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

如果是灰烬数据

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 2015年

以及如何将查询参数传递给beforeModel,afterModel和setController挂钩?
ahnbizcad 2015年

2
您仍然可以拥有多个模型,并且不会断开链接。如果您有两个动态细分,请传入ID(如果您是通过serialize:路由中的钩子基于子段来构建模型,则传入字符串),而不是在链接中传入模型。仅供参考,在模板中访问模型属性的语法为model.model1.somePropertymodel.puppyModel.someOtherProperty
ahnbizcad

您肯定可以,上面的大多数查询都不是动态细分,并且大多数时候您都希望从不同的路径中获取不同路径中的一堆不同模型,而只是为了正确设置链接(一次再次适用于非动态模型)。
Kingpin2k 2015年

3

我使用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方式
adriaan 2014年

这会返回Ember.RSVP的一系列已解决的Promise,为什么不是ember方法?也许不是余烬数据方式,但我没有使用余烬数据。您可以在此处查看文档:emberjs.com/api/classes/RSVP.Promise.html
la_antorcha 2014年

是的,我的意思是Ember Data方式,请忽略我的第一条评论
adriaan 2014年

1
这不能按预期方式工作。then函数中的逻辑没有任何区别。您正在弄乱异步代码并返回。
jelhan

2

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

2

最新版本的JSON-API如果您不介意修改API端点,那么Ember Data v1.13中实现很好地支持了同一请求中不同资源的捆绑。

就我而言,我有一个 session端点。会话与用户记录有关,而用户记录与我一直希望始终加载的各种模型有关。只需一个请求就可以了。

每个规范的一个警告是,您返回的所有实体都应以某种方式链接到所接收的主要实体。我相信,在规范化JSON时,ember-data只会遍历显式关系。

对于其他情况,我现在选择推迟加载其他模型,直到页面已加载为止,即对于单独的数据面板或其他内容,因此至少要尽快呈现页面。这样做会导致某些丢失/更改,要考虑“自动”错误加载状态。


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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.