Joe B
Joe B

Reputation: 1271

get length of hasMany relationship without triggering fetch

model:

DS.Model.extend({
  title: DS.attr('string'),
  body: DS.attr('string'),
  comments: DS.hasMany('comment', { async: true} ),

  hasComments: Ember.computed.gt('comments.length', 0)
});

payload:

{ 
  "id": "abcdefg",
  "title": "some cats are cool",
  "body": "",
  "comments: ["100", "101", "102"]
}

But the hasComments computed property triggers a fetch for each comment individually.. I don't want this :D

I know this works (avoids the fetch), but reaches into private API:

hasComments: Ember.computed.gt('data.comments.length', 0)

ember.js 1.8.1

ember-data 1.0.0-beta.11

Any other recommendations on achieving a computed property based off of the length

Upvotes: 5

Views: 1267

Answers (3)

CraigTeegarden
CraigTeegarden

Reputation: 8251

As of Ember Data 2.5 (mentioned in release notes) there is a feature called ds-references which provides a way to check if if a hasMany relationship contains items or not without triggering a fetch for those items:

export default Model.extend({
  title: DS.attr('string'),
  body: DS.attr('string'),
  comments: DS.hasMany('comment', { async: true} ),

  hasComments: Ember.computed('comments', function() {
    return this.hasMany('comments').ids().length > 0;
  })
});

See this working in an Ember Twiddle

The ds-references feature implements the references API as described in RFC 57. References is a low level API to perform meta-operations on records, has-many relationships and belongs-to relationships:

  • get the current local data synchronously without triggering a fetch or producing a promise
  • notify the store that a fetch for a given record has begun, and provide a promise for its result
  • similarly, notify a record that a fetch for a given relationship has begun, and provide a promise for its result
  • retrieve server-provided metadata about a record or relationship

Source: Ember forum post and Ember Data 2.5 release blog post

Upvotes: 6

stephen.hanson
stephen.hanson

Reputation: 9624

I wish there was an easy way to do this. The simplest way I can think of is to override extractHasMany in your serializer and save a new property, commentsCount, on your model, eg:

models/post.rb:

DS.Model.extend({
  title: DS.attr('string'),
  body: DS.attr('string'),
  comments: DS.hasMany('comment', { async: true} ),
  commentsCount: DS.attr('number'),

  hasComments: Ember.computed.gt('commentsCount', 0)
});

serializers/post.rb:

export default ApplicationSerializer.extend({
  normalizeHash: {
    posts(hash) {
      hash.comments_count = hash.comments.length;
      return hash;
    }
  }
}

Be careful though, as the commentsCount property will not automatically recompute in this case when you add/remove comments. You could probably build a computed property that uses the actually comments instead of commentsCount if the comments are loaded.

Upvotes: 0

jnfingerle
jnfingerle

Reputation: 713

You mentioned, that each comment was fetched individually. This can be avoided by setting coalesceFindRequests on the RestAdapter, if your backend supports this.

Yet, you'd still have one request too many. I don't think, that that can be avoided without some kind of dirty trick, but am open to learn from other answers.

Upvotes: 0

Related Questions