Search code examples
ember.jsember-datahas-manycomputed-properties

Ember computed property for retrieving one record out of a hasMany relationship?


Here's my situation, simplified:

// model/price-source.js

export default DS.Model.extend({
  price: DS.attr('number'),
  product: DS.belongsTo('product')
)};

// model/product.js

export default DS.Model.extend({
  priceSources: DS.hasMany('price-source')
)};

In my products template, I want to be able to simply refer to the source with the lowest price, like so:

// templates/products.hbs

{{#each model as |product|}}
<span>{{product.cheapestSource.price}} €</span>
{{/each}}

How would I go about setting up the cheapestSource computed property? I imagine I'd have to do something like this:

// model/product.js

  cheapestSource: Ember.computed('priceSources', function() {
    let sources = this.get('priceSources');
    let cheapest = sources.get('firstObject');

    // iterate over sources and set cheapest to whichever has the lowest price

    return cheapest;
  })

The problem is, I have little idea how to loop through the hasMany relationship (apart from using the handlebars {{#each}} helper), and whether a computed property can even consist of a single Ember Data record from another model. Does sources.@each somehow play into this, if so, how?

Any help and ideas are appreciated, thanks.


Solution

  • I got it working by sorting the priceSources into a computed property sortedPrices, then calling the firstObject of the sortedPrices in the template. Will edit this post with the actual solution soon.

    It took ages to test because I didn't realize that commenting out handlebars blocks will break the rendering of html inside them. Note to self...


    EDIT: This did it:

    export default DS.Model.extend({
      priceSources: DS.hasMany('price-source'),
      sortProperties: ['price:asc'],
      sortedSources: Ember.computed.sort('priceSources', 'sortProperties')
    });
    

    Then in the template:

    <span>{{product.sortedSources.firstObject.price}} €</span>
    

    Works ok, without a ton of code.