EmberJS computed.sort - sorting by associated mode

2019-07-20 00:20发布

问题:

I have a simple belongsTo model relationship:

contract.js:

export default DS.Model.extend({
    object               : DS.belongsTo('object'),
    ....
})

object.js:

export default DS.Model.extend({
    street              : DS.attr('string'),
    zip                 : DS.attr('string'),
    city                : DS.attr('string'),
    ...
})

In my controller for an entity that holds many contracts, I'd like to sort by the street name of the associated object, but somehow this

export default Ember.Controller.extend({

    sortOrder: ['object.street'],
    sortedObjects: Ember.computed.sort('model.contracts', 'sortOrder')

    ...
});

doesn't work.

Using a custom comparator function a la

function(a, b){
    if (a.street > b.street) {
      return 1;
    } else if (a.street < b.street) {
      return -1;
    }
}

I found out that a and b are Promises, but somehow I don't see how to get them sorted via a nested attribute (the object's street)

Edit

To clarify the code a little more:

contracts : Ember.computed.alias('model.contracts'),
street: Ember.computed.alias('realty.street'),

sortOrder: ['realty.street'],
sortedOwnedRealties: Ember.computed.sort('contracts.@each.street', function (a, b) {
    console.log(Ember.get(a, 'id'));
    console.log(Ember.get(a, 'street'));

    //return 1;
})

That function prints undefined to the console for the street, but the correct id.

I've renamed object to realty for clarity.

回答1:

They are probably PromiseObjects, which are both, Ember.Object and Promise. There are a few things you do that might be not a good idea:

First object is a reserved keyword. It's not a good idea to have a model or relationship named object, even if it might work.

Second your computed property has the wrong dependency key. Actually you would want something like 'modelcontracts.@each.object.street' instead of 'modelcontracts', however this will not work because '@each.a.b' is not supported. The solution is to create an alias to the street on the contract:

street: Ember.computed.alias('object.street'),

And then you can use this as your dependency key: modelcontracts.@each.street.

Next in your custom comparator function you access a.street by dot notation. This will not work and is unreliable for ember objects. Always use .get() to access the properties:

import Ember form 'ember';
const {get} = Ember;


function(a, b){
  if(get(a, 'street') > get(b, 'street')) {...}
}

However also notice that without the computed.alias I've mentioned above it's not get(a, 'street') but get(a, 'object.street').


The problem why your code is not working is probably that object is loaded async, and so at the time your CP is evaluated the street is not yet loaded. Adding the correct dependency key will probably fix this.