简体   繁体   English

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

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

While I am not new to web development, I am quite new to to client-side MVC frameworks.虽然我对 Web 开发并不陌生,但我对客户端 MVC 框架还是很陌生。 I did some research and decided to give it a go with EmberJS.我做了一些研究,并决定尝试使用 EmberJS。 I went through the TodoMVC guide and it made sense to me...我浏览了 TodoMVC 指南,这对我来说很有意义......

I have setup a very basic app;我已经设置了一个非常基本的应用程序; index route, two models and one template.索引路由,两个模型和一个模板。 I have a server-side php script running that returns some db rows.我有一个服务器端 php 脚本正在运行,它返回一些 db 行。

One thing that is very confusing me is how to load multiple models on the same route.让我非常困惑的一件事是如何在同一条路线上加载多个模型。 I have read some information about using a setupController but I am still unclear.我已经阅读了一些关于使用 setupController 的信息,但我仍然不清楚。 In my template I have two tables that I am trying to load with unrelated db rows.在我的模板中,我有两个表,我试图用不相关的数据库行加载它们。 In a more traditional web app I would have just issued to sql statements and looped over them to fill the rows.在更传统的 Web 应用程序中,我只会向 sql 语句发出并循环遍历它们以填充行。 I am having difficulty translating this concept to EmberJS.我很难将这个概念翻译成 EmberJS。

How do I load multiple models of unrelated data on the same route?如何在同一路线上加载多个不相关数据模型?

I am using the latest Ember and Ember Data libs.我正在使用最新的 Ember 和 Ember Data 库。

Update更新

although the first answer gives a method for handling it, the second answer explains when it's appropriate and the different methods for when it isn't appropriate.虽然第一个答案给出了处理它的方法,但第二个答案解释了何时合适以及何时不合适的不同方法。

BEWARE:当心:

You want to be careful about whether or not returning multiple models in your model hook is appropriate.您需要注意在模型挂钩中返回多个模型是否合适。 Ask yourself this simple question:问自己这个简单的问题:

  1. Does my route load dynamic data based on the url using a slug :id ?我的路由是否使用 slug :id基于 url 加载动态数据? ie this.resource('foo', {path: ':id'});this.resource('foo', {path: ':id'});

If you answered yes如果你回答是

Do not attempt to load multiple models from the model hook in that route!!!不要尝试从该路径中的模型钩子加载多个模型!!! The reason lies in the way Ember handles linking to routes.原因在于 Ember 处理路由链接的方式。 If you provide a model when linking to that route ( {{link-to 'foo' model}} , transitionTo('foo', model) ) it will skip the model hook and use the supplied model.如果您在链接到该路由时提供模型( {{link-to 'foo' model}} , transitionTo('foo', model) ),它将跳过模型挂钩并使用提供的模型。 This is probably problematic since you expected multiple models, but only one model would be delivered.这可能是有问题的,因为您期望有多个模型,但只会交付一个模型。 Here's an alternative:这是一个替代方案:

Do it in setupController / afterModelsetupController / 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'});
  }
});

Example: http://emberjs.jsbin.com/cibujahuju/1/edit示例: http : //emberjs.jsbin.com/cibujahuju/1/edit

If you need it to block the transition (like the model hook does) return a promise from the afterModel hook.如果你需要它来阻止转换(就像模型钩子那样)从afterModel钩子返回一个承诺。 You will need to manually keep track of the results from that hook and hook them up to your controller.您需要手动跟踪来自该挂钩的结果并将它们连接到您的控制器。

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

Example: http://emberjs.jsbin.com/diqotehomu/1/edit示例: http : //emberjs.jsbin.com/diqotehomu/1/edit

If you answered no如果你回答没有

Go ahead, let's return multiple models from the route's model hook:继续,让我们从路由的模型钩子中返回多个模型:

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return {
           model1: ['red', 'yellow', 'blue'],
           model2: ['green', 'purple', 'white']
    };
  }
});

Example: http://emberjs.jsbin.com/tuvozuwa/1/edit示例: http : //emberjs.jsbin.com/tuvozuwa/1/edit

If it's something that needs to be waited on (such as a call to the server, some sort of promise)如果它需要等待(例如调用服务器,某种承诺)

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

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

In the case of Ember Data在 Ember 数据的情况下

App.IndexRoute = Ember.Route.extend({
  var store = this.store;
  model: function() {
    return Ember.RSVP.hash({
           cats: store.find('cat'),
           dogs: store.find('dog')
    });
  }
});

Example: http://emberjs.jsbin.com/pekohijaku/1/edit示例: http : //emberjs.jsbin.com/pekohijaku/1/edit

If one is a promise, and the other isn't, it's all good, RSVP will gladly just use that value如果一个是承诺,另一个不是,那都很好,RSVP 很乐意使用这个值

App.IndexRoute = Ember.Route.extend({
  var store = this.store;
  model: function() {
    return Ember.RSVP.hash({
           cats: store.find('cat'),
           dogs: ['pluto', 'mickey']
    });
  }
});

Example: http://emberjs.jsbin.com/coxexubuwi/1/edit示例: http : //emberjs.jsbin.com/coxexubuwi/1/edit

Mix and match and have fun!混合搭配,玩得开心!

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

Example: http://emberjs.jsbin.com/joraruxuca/1/edit示例: http : //emberjs.jsbin.com/jorauxuca/1/edit

NOTE: for Ember 3.16+ apps, here is the same code, but with updated syntax / patterns: https://stackoverflow.com/a/62500918/356849注意:对于 Ember 3.16+ 应用程序,这里是相同的代码,但更新了语法/模式: https ://stackoverflow.com/a/62500918/356849

The below is for Ember < 3.16, even though the code would work as 3.16+ as fully backwards compatible, but it's not always fun to write older code.下面是针对 Ember < 3.16 的,尽管代码可以像 3.16+ 一样完全向后兼容,但编写旧代码并不总是很有趣。


You can use the Ember.RSVP.hash to load several models:您可以使用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);
  }
});

And in your template you can refer to people and companies to get the loaded data:在您的模板中,您可以参考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>

This is a Twiddle with this sample: https://ember-twiddle.com/c88ce3440ab6201b8d58这是带有此示例的 Twiddle: https ://ember-twiddle.com/c88ce3440ab6201b8d58

I use something like the answer that Marcio provided but it looks something like this:我使用类似于 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;
    });  

Taking the accepted answer, and updating it for Ember 3.16+接受接受的答案,并为 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 };
  }

}

Note, it's recommended to not use setupController to setup aliases, as it obfuscates where data is coming from and how it flows from route to template.请注意,建议不要使用 setupController 来设置别名,因为它会混淆数据的来源以及它如何从路由流向模板。

So in your template, you can do:因此,在您的模板中,您可以执行以下操作:

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

If you use Ember Data, it gets even simpler for unrelated models:如果您使用 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)
    });
  }
});

If you only want to retrieve an object's property for model2 , use DS.PromiseObject instead of DS.PromiseArray :如果您只想为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'))
    });
  }
});

The latest version of JSON-API as implemented in Ember Data v1.13 supports bundling of different resources in the same request very well, if you don't mind modifying your API endpoints.如果您不介意修改 API 端点,那么 Ember Data v1.13 中实现的最新版本的JSON-API可以很好地支持在同一请求中捆绑不同的资源。

In my case, I have a session endpoint.就我而言,我有一个session端点。 The session relates to a user record, and the user record relates to various models that I always want loaded at all times.会话与用户记录有关,而用户记录与我一直希望始终加载的各种模型有关。 It's pretty nice for it all to come in with the one request.这一切都与一个请求一起进来真是太好了。

One caveat per the spec is that all of the entities you return should be linked somehow to the primary entity being received.每个规范的一个警告是,您返回的所有实体都应该以某种方式链接到正在接收的主要实体。 I believe that ember-data will only traverse the explicit relationships when normalizing the JSON.我相信 ember-data 在规范化 JSON 时只会遍历显式关系。

For other cases, I'm now electing to defer loading of additional models until the page is already loaded, ie for separate panels of data or whatever, so at least the page is rendered as quickly as possible.对于其他情况,我现在选择推迟加载其他模型,直到页面已经加载,即对于单独的数据面板或其他任何内容,因此至少页面会尽快呈现。 Doing this there's some loss/change with the "automatic" error loading state to be considered.这样做会有一些损失/变化,需要考虑“自动”错误加载状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM