Ember 等待关系以 ember 并发加载到数组中
Ember waiting for relationships to load in array with ember concurrency
我正在尝试根据其中一个相关模型对一组模型对象进行排序。
我有一个 Document,其中 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 并发来等待所有关系加载后再对数据进行排序。
这是我的控制器:
~/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,不正确。在初始化第一个任务 运行s 时,但不会等待为每个订单项加载位置。我试图让它加载第二个任务中的所有位置,但显然不是正确的方法。因此,在初始化时,它会 运行 通过排序,但是 _.groupBy 的函数将 return 未定义,因为此时 location 未定义。然后,如果我在知道位置已加载(因为我在模板的其他地方使用它们)后再次手动 运行 任务(使用模板上的按钮),它工作正常。
解决方案不必使用 ember 并发,它似乎是正确的工具,但我认为它也可以用 promises 来完成。
好问题!
这是我的做法。
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
与否:
- 这种方法会产生大量的网络请求,除非您使用记录不完整的网络请求分组功能(并且您的后端支持它)。
- 如果任何请求失败,您的应用将进入不一致状态。大概整个并发task/computed属性都会失败,用户将没有机会重新启动失败的请求
- 您的页面将过早呈现,没有任何内容。然后内容最终会出现。
当然,这三个问题都可以解决,但这是一个 PITA。 正确的方法是在路由的模型挂钩中预加载关系。
以这样一种方式组织它,即只需几个网络请求即可获取所有必要的记录。使用 JSONAPI 过滤器或创建自定义 API 端点来服务所有相关记录。
在最坏的情况下,仍然会发出很多请求,但在路由的模型挂钩中执行,该挂钩将处理异步并将记录(而不是承诺)送入 controller/template。 预加载相关记录后,您可以像访问同步关系一样访问关系。
我正在尝试根据其中一个相关模型对一组模型对象进行排序。
我有一个 Document,其中 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 并发来等待所有关系加载后再对数据进行排序。
这是我的控制器:
~/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,不正确。在初始化第一个任务 运行s 时,但不会等待为每个订单项加载位置。我试图让它加载第二个任务中的所有位置,但显然不是正确的方法。因此,在初始化时,它会 运行 通过排序,但是 _.groupBy 的函数将 return 未定义,因为此时 location 未定义。然后,如果我在知道位置已加载(因为我在模板的其他地方使用它们)后再次手动 运行 任务(使用模板上的按钮),它工作正常。
解决方案不必使用 ember 并发,它似乎是正确的工具,但我认为它也可以用 promises 来完成。
好问题!
这是我的做法。
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
与否:
- 这种方法会产生大量的网络请求,除非您使用记录不完整的网络请求分组功能(并且您的后端支持它)。
- 如果任何请求失败,您的应用将进入不一致状态。大概整个并发task/computed属性都会失败,用户将没有机会重新启动失败的请求
- 您的页面将过早呈现,没有任何内容。然后内容最终会出现。
当然,这三个问题都可以解决,但这是一个 PITA。 正确的方法是在路由的模型挂钩中预加载关系。
以这样一种方式组织它,即只需几个网络请求即可获取所有必要的记录。使用 JSONAPI 过滤器或创建自定义 API 端点来服务所有相关记录。
在最坏的情况下,仍然会发出很多请求,但在路由的模型挂钩中执行,该挂钩将处理异步并将记录(而不是承诺)送入 controller/template。 预加载相关记录后,您可以像访问同步关系一样访问关系。