2016-12-03 16 views
1

我试图根据其相关模型之一对模型对象的数组进行排序。Ember等待关系加载与阵列与烬并发

我有一个文档的hasMany的LineItem的LineItem属于关联一个位置。我想根据其位置对行项目进行排序。所以,如果我有这样的事情:

let lineItems = [ 
    { 
    itemName: "One", 
    location: { 
     name: "Kitchen" 
    } 
    }, 
    { 
    itemName: "Two", 
    location: { 
     name: "Kitchen" 
    } 
    }, 
    { 
    itemName: "Three", 
    location: { 
     name: "Bathroom" 
    } 
    }, 
    { 
    itemName: "Four", 
    location: { 
     name: "Bathroom" 
    } 
    }, 
    { 
    itemName: "Five", 
    location: { 
     name: "Hall" 
    } 
    }, 
    { 
    itemName: "Six", 
    location: { 
     name: "Hall" 
    } 
    } 
]; 

我愿意去:

let groupedLineItems = { 
    "Kitchen": [ 
    { 
     itemName: "One", 
     location: "Kitchen" 
    }, 
    { 
     itemName: "Two", 
     location: "Kitchen" 
    }, 
    ], 
    "Bathroom": [ 
    { 
     itemName: "Three", 
     location: "Bathroom" 
    }, 
    { 
     itemName: "Four", 
     location: "Bathroom" 
    }, 
    ], 
    "Hall": [ 
    { 
     itemName: "Five", 
     location: "Hall" 
    }, 
    { 
     itemName: "Six", 
     location: "Hall" 
    } 
    ] 
} 

我已经想通了如何排序,但只有一次,所有的关系都加载。我无法弄清楚如何在排序数据之前使用ember concurrency来等待所有关系加载。

这是我的控制器:

~/app/controllers/document.js 

import Ember from 'ember'; 
import _ from 'underscore'; 
import { task } from 'ember-concurrency'; 
const { computed } = Ember; 

export default Ember.Controller.extend({ 
    init() { 
     this.get('myTask').perform(); 
    }, 

    myTask: task(function *() { 
     // Wait for locations to resolve. 
     let lineItems = this.get('model').get('lineItems'); 

     yield this.get("secondTask").perform(lineItems); 

     let groupedLineItems = yield _.groupBy(lineItems.toArray(), (lineItem) => { 
      lineItem.get('location').then((location) => { 
       return location.get('name'); 
      }); 
     }); 

     this.set("groupedLineItems2", Ember.Object.create(groupedLineItems)); 
    }), 

    secondTask: task(function * (lineItems) { 
     yield lineItems.toArray().forEach((lineItem) => { 
      lineItem.get('location'); 
     }); 
    }) 
}); 

这是WIP控制器上,而不是正确的。在init中,第一个任务运行,但不会等待每个行项目的位置加载。我试图让它加载第二个任务中的所有位置,但显然不是正确的方法。因此,在init中,它将通过排序来运行,但_.groupBy的函数将返回undefined,因为该位置的位置未定义。然后,如果我知道位置已加载后(因为我在模板的其他地方使用它们)手动再次手动执行任务(使用模板上的按钮),它可以正常工作。

解决方案不必使用ember并发性,它看起来像是正确的工具,但我认为它也可以用promise来完成。

+0

这不是“排序”。 – 2016-12-04 05:41:24

回答

1

伟大的问题!

下面是我该怎么做。

models/document.js

import Ember from 'ember'; 
import Model from "ember-data/model"; 
import attr from "ember-data/attr"; 
import { belongsTo, hasMany } from "ember-data/relationships"; 

export default Model.extend({ 
    lineItems: hasMany('line-item'), 

    groupedLineItemsPromise: Ember.computed(function() { 
    return this 
     .get('lineItems') 
     .then(lineItems => { 
     const locations = Ember.RSVP.all(lineItems.map(item => item.get('location'))) 
     return Ember.RSVP.hash({lineItems, locations}) 
     }) 
     .then(({lineItems}) => { 
     return lineItems.reduce((result, item) => { 
      const locationName = item.get('location.name') 
      result[locationName] = result[locationName] || [] 
      result[locationName].pushObject(item) 
      return result 
     }, {}) 
     }) 
    }), 

    groupedLineItemsProxy: Ember.computed('groupedLineItemsPromise', function() { 
    return Ember 
     .Object 
     .extend(Ember.PromiseProxyMixin) 
     .create({ 
     promise: this.get('groupedLineItemsPromise') 
     }) 
    }) 
}); 

演示:https://ember-twiddle.com/3f7ee2371c424c456c48347774395191?openFiles=models.document.js%2C

注意,你并不真的需要ember-concurrency这一点。我使用好的旧计算属性来实现结果。

但问题是,这是摆在首位,ember-concurrency或没有错误的做法:

  1. 这种做法将产生吨的网络请求的,除非你使用的分组网络请求的不良记录功能(你的后端支持它)。
  2. 如果任何请求失败,您的应用将进入不一致的状态。可能整个并发任务/计算属性将失败,并且用户将没有机会重新启动失败的请求。
  3. 你的页面会过早渲染,没有内容。然后内容将最终出现。

当然,所有三个问题都可以解决,但它是一个PITA。执行此操作的正确方法是预加载路径模型钩子中的关系。

以这样一种方式组织它,即只需几个网络请求即可获取所有必需的记录。可以使用JSONAPI筛选器,也可以创建一个自定义API端点来提供所有相关记录。

在最坏的情况下,仍然做很多请求,但是在路由的模型钩子中执行它,它将处理控制器/模板中的异步和馈送记录,而不是承诺。 一旦预先加载了相关记录,就可以像访问关系一样来访问关系

+0

谢谢,这么好的答案! 最后,我发现我需要做的: '返回this.get( '存储')findRecord( '文件',params.document_id,{包括: 'lineItems.location'});' 在。我的模型钩在路线上。然后我可以这样做: 'groupedLineItems:computed('model.lineItems。@ each.location',function(){ const lineItems = this.get('model')。get('lineItems')。toArray ); return this.sortObject(_。groupBy(lineItems,(lineItem)=> lineItem.get('location')。get('name'))); })' – brownie3003