如何使用 EmberData 在 EmberJS 中返回由嵌套模型组成的承诺? [英] How to return a promise composed of nested models in EmberJS with EmberData?

查看:17
本文介绍了如何使用 EmberData 在 EmberJS 中返回由嵌套模型组成的承诺?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

# Ember       : 1.4.0
# Ember Data  : 1.0.0-beta.7+canary.b45e23ba

型号

我简化了我的用例,使问题更容易理解和回答.假设我们有 3 个模型:CountryRegionArea:

Country:
  - id: DS.attr('number')
  - name: DS.attr('string')
  - regions: DS.hasMany('region')

Region:
  - id: DS.attr('number')
  - name: DS.attr('string')
  - country: DS.belongsTo('country')
  - areas: DS.hasMany('area')

Area:
  - id: DS.attr('number')
  - name: DS.attr('string')
  - region: DS.belongsTo('region')

预期结果

Route 的模型钩子应该返回一个对象数组.像这样:

Expected results

The Route's model hook should return an array of objects. Like this:

注意:缩进只是使示例更具可读性.

Note: The indentations are only to make the example more readable.

 Country I
   Region A
      Area 1
      Area 2
   Region B
      Area 3
 Country II
   Region C
      Area 4
 Country III
   Region D
      Area 5

目前的方法

App.MyRoute = Ember.Route.extend({
  model: function() {
    return this.store.find('country').then(function(countries){
      // promise all counties

      // map resolved countires into an array of promises for owned regions
      var regions = countries.map(function(country){
        return country.get('regions');
      });

      // map resolved regions into an array of promises for owned areas
      var areas = regions.then(function(regions){
        return regions.map(function(region){
          return region.get('areas');
        });
      });

      // do not return until ALL promises are resolved
      return Ember.RSVP.all(countries, regions, areas).then(function(data){
        // here somehow transform the data into expected output format and return it
      });
    });
  }
)};

错误

我收到 Error while loading route: TypeError: Object [object Array] has no method 'then' 这显然来自这个代码:

Error

I'm getting Error while loading route: TypeError: Object [object Array] has no method 'then' which obviously comes from this code:

var regions = countries.map(function(country){
  return country.get('regions');
});
var areas = regions.then(function(regions){
// regions is not a promise

然而,这应该表明我遇到的真正问题:

However this should show the real problem I have:

我需要解析 countries 来获取 regions,然后我需要获取 areas.我一直在检查 RSVP.hashRSVP.all 函数,阅读官方 API 并观看 这个谈话,但是我有点未能创建正确的代码来链接承诺,并在最终 then 修改返回的结果以匹配我的期望.

I need countries resolved to get the regions, which in turn I need to get the areas. I've been checking the RSVP.hash and RSVP.all functions, reading the official API and watching this talk, however I somewhat fail to create the correct code to chain promises and in the final then modify the returned result to match my expectations.

有人告诉我,像这样加载数据可能会导致许多 HTTP 请求,可能通过旁加载可以更好地解决这个问题,但是:

I have been told that loading data like this may cause many HTTP requests and probably this would be solved better by sideloading, but:

  • 此时,我使用 FixturesAdapter,所以 HTTP 请求不是问题
  • 我真的很想更好地理解 RSVP 和 Promises
  • at this moment, I use FixturesAdapter, so HTTP requests are not an issue
  • I really want to understand RSVP and Promises better

这就是为什么弄清楚应该如何正确完成这件事对我来说很重要.

Thats why it is important to me to figure out how this should be done correctly.

我为我的示例创建了一个 JSBin由 kingpin2k 的 anwser 推荐.

I've created a JSBin for my example with changes suggested by kingpin2k's anwser.

虽然代码有效,但结果...出乎意料:

While the code works, the results are... unexpected:

  • countries 数组中,我找到了 countryregion 对象.为什么?
  • countryregion 对象似乎已加载,但区域未加载(请参阅 JSBin 中的控制台日志结果).. 为什么?强>
  • in the countries array I found both country and region objects. Why?
  • the country and region objects seem to be loaded, but area's don't (see console log results in the JSBin).. Why?

所以我终于注意到我在什么地方偏离了 Ember 的正道.Kingpin2k 的 anwser 向前迈进了一大步,但它包含一个小错误:

So I've finally noticed where I went astray from the righteous path of Ember. Kingpin2k's anwser was a huge step forward, but it contains a little error:

return this.store.find('country').then(function(countries){
  // this does not return an array of regions, but an array of region SETs
  var regionPromises = countries.getEach('regions');

  // wait for regions to resolve to get the areas
  return Ember.RSVP.all(regionPromises).then(function(regions){
    // thats why here the variable shouldn't be called "regions"
    // but "regionSets" to clearly indicate what it holds

    // for this example i'll just reassign it to new var name
    var regionSets = regions;

    // now compare these two lines (old code commented out)
    //var areaPromises = regions.getEach('areas'); 
    var areaPromises = regionSets.getEach('areas');

    // since regionSet does not have a property "areas" it
    // won't return a promise or ever resolve (it will be undefined)

    // the correct approach would be reduceing the array of sets
    // an array of regions
    var regionsArray = regionSets.reduce(function(sum, val){
      // since val is a "Ember.Set" object, we must use it's "toArray()" method
      // to get an array of contents and then push it to the resulting array
      return sum.pushObjects(val.toArray());
    }, []);

    // NOW we can get "areas"
    var realAreaPromises = regionsArray.getEach('areas');

    // and now we can use Ember.RSVP to wait for them to resolve
    return Ember.RSVP.all(realAreaPromises).then(function(areaSets){
    // note: here again, we don't get an array of areas
    // we get an array of area sets - each set for the corresponding region
      var results = [];

所以..现在我终于正确解析了所有对象(国家、地区、地区)并且可以继续我的工作:)

So.. now I've finally got all the objects correctly resolved (countries, regions, areas) and can continue my work :)

推荐答案

诀窍是您需要先解决某些承诺,然后才能访问这些记录的属性.Ember.RSVP.all 接受一组承诺.Ember.RSVP.hash 获取 promise 的哈希值.不幸的是,在之前的承诺得到解决之前,你无法构建你的承诺(唉,你不知道要获得哪个 regions 直到 countries 已解析,并且在解析 regions 之前您不知道要获取哪些 areas).在这种情况下,您确实有一系列要获取的承诺(尽管每个级别的承诺数组).Ember 知道要等到最深的承诺得到解决,并使用该值作为模型.

The trick is you need to resolve certain promises before you can access the properties on those records. Ember.RSVP.all takes an Array of promises. Ember.RSVP.hash takes a hash of promises. Unfortunately you're in the situation where you can't construct your promises until the previous promises have resolved (a la, you don't know which regions to get until the countries are resolved, and you don't know which areas to get until the regions are resolved). That being the case you really have a serial set of promises to fetch (albeit arrays of promises at each level). Ember knows to wait until the deepest promise has resolved and to use that value as the model.

现在我们需要假设 regionsarea 是异步的,如果它们不是,你告诉 Ember Data 信息将包含在请求中country,或者在带有 region 的请求中,这些集合不会是承诺,所以我在下面包含的代码将不起作用.

Now we need to pretend that regions and area are async, if they aren't, you're telling Ember Data the information will be included in the request with country, or in the request with region and those collections won't be promises so the code I've included below wouldn't work.

regions: DS.hasMany('region', {async: true})

areas: DS.hasMany('area', {async: true})

App.IndexRoute = Ember.Route.extend({
  controllerName: 'application',

  model: function() {
    return this.store.find('country').then(function(countries){
      // get each country promises
      var regionCollectionPromises = countries.getEach('regions');

      // wait for regions to resolve to get the areas
      return Ember.RSVP.all(regionCollectionPromises).then(function(regionCollections){

        var regions = regionCollections.reduce(function(sum, val){
            return sum.pushObjects(val.toArray());
        }, []);

        var areaCollectionPromises = regions.getEach('areas');
        //wait on the areas to resolve
        return Ember.RSVP.all(areaCollectionPromises).then(function(areaCollections){

          // yay, we have countries, regions, and areas resolved

          return countries;
        });
      });
    });

  }
});

说了这么多,因为看起来你正在使用 Ember Data,我只需要返回 this.store.find('country') 并让 Ember Data 在使用时获取数据...这个模板可以在没有所有承诺代码的情况下工作,并且会随着 Ember Data 自己履行承诺而填充(它会在看到您尝试使用数据时请求数据,很好的延迟加载).

All this being said, since it appears you're using Ember Data, I'd just return this.store.find('country') and let Ember Data fetch the data when it's used... This template would work without all of that promise code, and would populate as Ember Data fulfill's the promises on its own (it will request the data once it sees you've attempted to use the data, good ol' lazy loading).

{{#each country in model}}
  Country: {{country.name}}
  {{#each region in country.regions}}
    Region: {{region.name}}
      {{#each area in region.areas}}
        Area: {{area.name}}
     {{/each}}
  {{/each}}
{{/each}}

这篇关于如何使用 EmberData 在 EmberJS 中返回由嵌套模型组成的承诺?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆