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

时间:2013-12-11 14:38:05

标签: javascript ember.js rsvp.js

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

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

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

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

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

更新

虽然第一个答案提供了处理它的方法,但第二个答案解释了何时适当,以及何时不合适的不同方法。

6 个答案:

答案 0 :(得分:149)

要注意:

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

  1. 我的路线是否使用slug :id根据网址加载动态数据?即 this.resource('foo', {path: ':id'});
  2. 如果您的回答是

    不要尝试从该路径中的模型钩子加载多个模型!!! 原因在于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

    如果是需要等待的东西(例如对服务器的调用,某种承诺)

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

    示例:http://emberjs.jsbin.com/xucepamezu/1/edit

    在Ember Data的情况下

    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

答案 1 :(得分:92)

您可以使用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

答案 2 :(得分: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;
    });  

答案 3 :(得分:2)

如果使用Ember Data,对于不相关的模型来说更简单:

<Grid Height="20" Width="200"  Background="LightCyan" VerticalAlignment="Center">
    <Path Stroke="Red" StrokeThickness="20" VerticalAlignment="Bottom">
        <Path.Data>
            <LineGeometry StartPoint="0,0" EndPoint="200,0"></LineGeometry>
        </Path.Data>
    </Path>
</Grid>

如果您只想检索install的对象属性,请使用DS.PromiseObject代替DS.PromiseArray

-r

答案 4 :(得分:2)

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

在我的情况下,我有一个session端点。会话与用户记录有关,用户记录与我一直想要加载的各种模型有关。只需要一个请求,这一切都非常好。

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

对于其他情况,我现在选择推迟加载其他模型,直到页面已经加载,即对于单独的数据面板或其他任何内容,因此至少页面尽可能快地呈现。这样做有一些损失/变化,需要考虑“自动”错误加载状态。

答案 5 :(得分: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>