如何根据异步关系在Ember Data模型上执行Ember.js计算属性?

时间:2014-05-24 23:37:04

标签: ember.js ember-data

我有一个Ember Data模型,我正在尝试根据async 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):

import { test, moduleForModel } from 'ember-qunit';
import Transaction from 'my-app/models/transaction';

moduleForModel('transaction', 'Unit - Transaction Model', {
    needs: ['model:split']
});

test('amount', function() {
    var transaction = this.subject();
    var store = this.store();
    transaction.get('splits').addObjects([
        store.createRecord('split', { amount: 250 }),
        store.createRecord('split', { amount: 1000 })
    ]);
    equal(transaction.get('amount'), 1250);
});

3 个答案:

答案 0 :(得分:5)

您的hasMany属性是异步的,因此它是一个承诺,其值必须可以使用然后方法访问。

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

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

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

});

答案 1 :(得分:3)

呵呵,我打了一个相当长的答案,只是为了意识到你做错了什么,然后因为没有意识到自己而笑得很开心。 :)自从我使用Ember-Data以来已经有很长一段时间了,但这就是我所拥有的。

首先,这与承诺无关。 Ember-Data有很多关系返回PromiseArray个对象。 PromiseArrayArrayProxy延伸。 ArrayProxy是一种异步填充和数组的方法,同时仍能够立即绑定到它。 ArrayProxy 指定应如何填充数组,因此PromiseArray使用承诺的事实无关紧要。与任何其他阵列一样绑定到ArrayProxy。当内容发生变化时(无论出于何种原因),您的绑定将会更新。

既然我们已经解决了这个问题,那么你做错了就是使用Ember计算助手错了。例如,Ember.comptued.sum应该添加一个数字数组。像这样:

numbers: [1,2,3,4,5],
sum: Ember.computed.sum('numbers')

Ember负责处理@each以及所有废话。但你没有直号,你有对象。所以你需要使用Ember.reduceComputed(或类似的东西)。你正在使用它,但你使用了错误的密钥。你加上.@each.amount,这不是恩伯所期待的。所以Ember默默地失败了,因为在幕后,它正在观看splits.@each.amount.@each财产。如果您想使用reduceComputed,您可以执行以下操作:

amount: Ember.reduceComputed('splits', { ... })

但我从未使用reduceComputed,我喜欢保持简单,所以试试这个:

amount: function() {
    return this.get('splits').reduce(function(sum, split) {
        return sum + split.get('amount');
    }, 0);
}.property('splits.@each.amount')

很抱歉答案很长,但我想解释一下我的解决方案,而不仅仅是解决问题。这是早期的,所以如果我搞砸了一些语法,请告诉我。但我认为你应该得到主要的想法。 :)

编辑:我把一个小的JSBin放在一起模拟你正在做的事情。你可以找到它here。您会看到objects属性是未解析的PromiseArray,就像您从Ember-Data hasMany关系中得到的那样。单击“解析”按钮后,承诺将解析,ArrayProxy更新以及计算的总更新。

答案 2 :(得分:0)

试试这种方式

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