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

EmberJS: How to load multiple models on the same route?

本文关键字:路径 加载 模型 一条路 一条 EmberJS      更新时间:2024-06-17

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

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

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

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

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

更新

虽然第一个答案给出了处理它的方法,但第二个答案解释了什么时候合适,以及什么时候不合适的不同方法。

硬件:

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

  1. 我的路由是否使用段塞:id基于url加载动态数据?即。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

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

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

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

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

我使用了类似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,那么对于不相关的模型,它会变得更简单:

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.Promise Array:

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

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

在我的情况下,我有一个session端点。会话与用户记录相关,用户记录与我一直希望加载的各种模型相关。很高兴所有人都能提出一个请求。

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

对于其他情况,我现在选择推迟加载其他模型,直到页面已经加载,即对于单独的数据面板或其他什么,这样至少页面可以尽快呈现。这样做会考虑"自动"错误加载状态的一些损失/更改。