我正在尝试根据其相关模型之一对模型对象进行排序。
我有文档, hasMany LineItem 和 LineItem belongsTo a 位置即可。我想按行位置对行项目进行排序。所以,如果我有这样的事情:
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并发来等待所有关系在排序数据之前加载。
这是我的控制者:
~/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并发,它看起来就像是正确的工具,但我认为它也可以通过promises完成。
答案 0 :(得分: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
或不是:
当然,所有这三个问题都可以解决,但它是一个PITA。 这样做的正确方法是在路径的模型钩子中预加载关系。
以一种只需几个网络请求即可获取所有必要记录的方式进行组织。使用JSONAPI过滤器或创建一个提供所有相关记录的自定义API端点。
在最坏的情况下,仍然会执行大量请求,但是在路由模型挂钩中执行此操作,该挂钩将处理异步并将记录而非承诺记录到控制器/模板中。 预先加载相关记录后,您可以像访问同步一样访问关系。