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

Posted

技术标签:

【中文标题】EmberJS:如何在同一条路线上加载多个模型?【英文标题】:EmberJS: How to load multiple models on the same route? 【发布时间】:2013-12-29 14:14:00 【问题描述】:

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

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

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

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

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

更新

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

【问题讨论】:

这能回答你的问题吗? Request Two Models together 【参考方案1】:

注意:

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

    我的路由是否使用 slug :id 基于 url 加载动态数据? IE。 this.resource('foo', path: ':id');

如果你回答是

不要尝试从该路由中的模型钩子加载多个模型!!!原因在于 Ember 处理链接到路由的方式。如果您在链接到该路由时提供模型(link-to 'foo' modeltransitionTo('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 钩子返回一个承诺。您将需要手动跟踪该钩子的结果并将它们连接到您的控制器。

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

【讨论】:

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

注意:对于 Ember 3.16+ 应用程序,这里是相同的代码,但具有更新的语法/模式:https://***.com/a/62500918/356849

以下适用于 Ember


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

【讨论】:

这种方法很好,前提是您的路线中没有任何动态路段。如果您有动态路段并且通过link-to 'index' someModel 输入路线,则完全跳过模型挂钩,这将破坏此示例。一种更强大的方法是在始终运行的 setupController 中加载任何额外的模型。 这正是我要找的! @EoinKelly 我实际上会在这里使用控制器#afterModel 事件,因为您可以从 afterModel 返回一个承诺并期望它的行为类似于模型,但不会跳过 afterModel。 如何访问 setupcontroller 中的动态段、参数或查询参数 feom? @Eoin Kelly :您可以通过传递 ID(或 slug)而不是模型来绕过它【参考方案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 方式 这会从 Ember.RSVP 返回一组已解决的承诺,为什么不是 ember 方式?也许不是 ember 数据方式,但我没有使用 ember 数据。您可以在此处查看文档:emberjs.com/api/classes/RSVP.Promise.html 没错,我说的是 Ember Data 方式,忽略我的第一条评论 这不能按预期工作。 then 函数中的逻辑没有任何区别。你搞砸了异步代码并返回。【参考方案4】:

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

【讨论】:

【参考方案5】:

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

【讨论】:

【参考方案6】:

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

就我而言,我有一个session 端点。会话与用户记录相关,而用户记录与我一直想要加载的各种模型相关。一个请求就可以满足所有需求。

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

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

【讨论】:

以上是关于EmberJS:如何在同一条路线上加载多个模型?的主要内容,如果未能解决你的问题,请参考以下文章

Ruby on Rails - 如何为同一操作添加第二条路线?

同一路线的角力载荷

EmberJS:更改加载模型的 url (ember-data)

EmberJs - 链接到多个动态路由,多个嵌套级别

在同一服务器和端口上运行 EmberJS 和 Django

在同一应用程序上使用 Passport Local 和 JWT 策略(在同一条路线上)