如何基于异步关系对Ember数据模型执行Ember.js计算属性?

如何基于异步关系对Ember数据模型执行Ember.js计算属性?,ember.js,ember-data,Ember.js,Ember Data,我有一个余烬数据模型,我正试图根据异步hasMany关系的属性来计算属性。由于某种原因,它似乎从未重新计算过。我怎样才能正确地做到这一点 守则: export default DS.Model.extend({ splits: DS.hasMany('split', { async: true }), amount: Ember.reduceComputed('splits.@each.amount', { initialValue: 0, add

我有一个余烬数据模型,我正试图根据异步hasMany关系的属性来计算属性。由于某种原因,它似乎从未重新计算过。我怎样才能正确地做到这一点

守则:

export default DS.Model.extend({
    splits: DS.hasMany('split', { async: true }),
    amount: Ember.reduceComputed('splits.@each.amount', {
        initialValue: 0,
        addedItem: function(accValue, split) { return accValue + split.get('amount'); },
        removedItem: function(accValue, split) { return accValue - split.get('amount'); }
    })
    /* Neither of these work either.
    amount: Ember.computed.sum('splits.@each.amount') // This doesn't work
    amount: Ember.computed('splits.@each.amount', function() {
        return this.get('splits').reduce(function(pValue, split) {
            return pValue + split.get('amount');
        }, 0);
    })
    */
});
测试失败(预期为
1350
,获得
0
):


hasMany属性是异步的,因此它是一个承诺,它的值必须可以通过then方法访问

transaction.get('splits').then(function(splits) {

  split = store.createRecord('split', { amount: 250 }),
  splits.pushObject(split);

  split = store.createRecord('split', { amount: 1000 })
  splits.pushObject(split);

});

呵呵,我打了一个相当长的答案,结果才意识到你做错了什么,然后因为自己没有意识到这一点而大笑起来我已经很久没有使用余烬数据了,但这里是我得到的

首先,这与承诺无关。余烬数据有许多返回对象的关系
PromiseArray
扩展自
ArrayProxy
。而
ArrayProxy
是一种异步填充和数组的方法,同时仍然能够立即绑定到它
ArrayProxy
没有指定如何填充数组,因此,
PromiseArray
使用承诺这一事实与此无关。像绑定任何其他数组一样绑定到
ArrayProxy
。当内容发生更改时(无论出于何种原因),您的绑定将更新

现在我们已经解决了这个问题,您所做的错误是错误地使用了Ember计算的帮助程序。例如,
Ember.comptued.sum
应该添加一个数字数组。大概是这样的:

numbers: [1,2,3,4,5],
sum: Ember.computed.sum('numbers')
amount: Ember.reduceComputed('splits', { ... })
余烬为您处理
@每一个
和所有这些废话。但你没有直接的数字,你有对象。因此,您需要使用
Ember.reduceComputerd
(或类似的东西)。你用的是那把钥匙,但你用错了。您添加了
@each.amount
,这不是余烬所期望的。所以余烬默默地失败了,因为在幕后,它正在观看
拆分@each.amount.@each
属性。如果要使用
reducecomputerd
,可以执行以下操作:

numbers: [1,2,3,4,5],
sum: Ember.computed.sum('numbers')
amount: Ember.reduceComputed('splits', { ... })
但我从未使用过
reducecomputer
,我喜欢保持简单,因此请尝试以下方法:

amount: function() {
    return this.get('splits').reduce(function(sum, split) {
        return sum + split.get('amount');
    }, 0);
}.property('splits.@each.amount')
很抱歉回答得太长,但我想解释我的解决方案,而不仅仅是给出它。现在还早,所以如果我搞砸了一些语法,请告诉我。但我认为你应该了解要点。:)

编辑:我放了一个小JSBin来模拟你正在做的事情。你可以找到它。您将看到
objects
属性是一个未解析的
PromiseArray
,就像您从一个Ember数据中得到的一样。单击“解决”按钮后,承诺将立即解决,
ArrayProxy
将更新,您计算的总更新也将更新。

尝试这种方式

DS.Model.extend({
    splits: DS.hasMany('split', { async: true }),
    splitAmountAry:Ember.computed.mapBy('amount'),
    amount:Ember.computed.sum('splitAmountAry')
})

因此,如果没有
DataBoundPromise
s,我能否在模板中使用
amount
。@Kerrick,我添加了第三个选项,使其与模板一起工作。我想,这就是你要找的。我想,DataBoundPromise就是这样让它开箱即用。事实上,我只是尝试了一下,在没有任何
amountPromise
PromiseProxyMixin
的模板中使用了
{amount}
,似乎效果很好,使得第1点错误,第3点不必要。看起来整个问题都是您在第2点中提到的关于使用
。然后在测试中使用
。是的,您是对的。使用拆分。@each确保根据加载的项计算属性并正确处理结果。实际上,这里使用
Ember.computed.sum
似乎是错误的。但是,在尝试您的计算属性(这在功能上等同于我的问题中的第三次尝试)之后,它仍然不起作用。看起来,这个谜题真正缺失的部分是@ppcano提出的使用
。然后
访问测试中的拆分。因此,尽管你指出了
Ember.computed.sum
的错误用法,并因此获得了一张赞成票,但我必须将答案和奖金奖励给@ppcano,因为你的答案本身无助于通过考试。此外,使用相依键
reduceComputed
将拆分。@each.amount'
在我使用
后似乎确实有效。然后