Data: How to filter models based on async relationships?

Created on 4 Feb 2015  路  27Comments  路  Source: emberjs/data

After having a lot of problems trying to get Ember to be happy with synchronous relationships, I've started to believe that async should be the default type of relationship for Ember models.

Only problem, is that some basic operations seem pretty strange when using async relationships!

Most recently I've hit a wall while trying to filter an array of objects based on properties of an object they are related to with an async relationship. Even though those models are loaded already, the abstraction of promises + the observer chain seem to prevent me from finding an elegant solution.

I have three different approaches I've tried, but they all have various pitfalls and caveats. I'm just looking for something that works. Does anyone have a solution to this?

Needs Bug Verification

Most helpful comment

Is this still an issue since references were introduced?

I don't believe so.

All 27 comments

That's good, because ED is going full async soon :)

I like to do what you're talking about this way:

femaleAuthoredLongPosts: Ember.computed.filter('posts', function (post) {
    return post.get('author').then(function(user){
        return !user.get('male') && post.get('body.length') > 10000;
    });
}).property('post.[]'),

Is that answering your problem? Is that even working in your case?

Hey @thaume, thanks for your reply. I definitely think going full async is the right decision. We just need solid reliable approaches to this kind of basic thing.

Your solution looks a lot like my second solution, and the problem is you're returning a Promise to a filter function, which does not seem to recognize the promise, but instead recognizes it as a truthy object, thus not filtering anything out.

I would point out that since this was something we both tried, some kind of promise-accepting filter would be a fantastic addition to Ember.

I thought I had a real clever solution here, but now the view won't even load with this:

femaleAuthoredLongPosts: function () {
  var posts = this.get('model');
  var promises = posts.map(function(post){
     return Em.RSVP.resolve(post);
   });

  var filterFn = function (post) {
    return post.get('user').then(function(user){
      return !user.get('male') && post.get('body.length') > 10000;
    });
  };

  return Em.RSVP.filter( promises, filterFn );
}.property('posts.[]')

unfortunately the above example is also somewhat incorrect, this makes me sad, as we are close but these ideas still need some serious fleshing out.

I have some plans to make Promise observable out-of-the-box, and also introduce the concept of a PromiseArray to make chaining sensible.

unfortunately this likely the verbose/correct and unfortunate way

femaleAuthoredLongPosts: Ember.computed('[email protected].{male,body.length}', function (post) {
    return DS.PromiseArray.create({
      promise: post.get('authors').then(function(user) {
        return !user.get('male') && post.get('body.length') > 10000;
      })
    });
}),

Don't be sad @stefanpenner, ED is so awesome right now. Should this problem be fixed on Ember or in ED?

I hadn't seen the .{bracket,observer} syntax before, but that's pretty cool.

I'm a little baffled by other aspects of this example, though, I'd appreciate if you could help me understand:

  • How is a normal Ember.computed able to know to pass in items within the array instead of the whole array.
  • How returning a whole PromiseArray per item, with a true/false resolution turns into a filter somehow.

I do think the idea of a PromiseArray with various promise-operating array functions is the way to go in the future for Ember. If only some company would just hire you to work on Ember all day :hand:

Oh, also, that DS.PromiseArray solution does the same thing as my previous "clever approach": The route doesn't even load anymore :(

Don't be sad @stefanpenner, ED is so awesome right now. Should this problem be fixed on Ember or in ED?

Ember. It's not really a problem, it is just terrible :P. Although improvements are planned its a time thing. :(

Well, I found a way to make it work. It's one of the ugliest, most anti-pattern chunks of Ember code I've ever written (lie), but unlike all the other proposals so far, this works:

// This is not a computed property, it's an array we update with an observer function:
femaleAuthoredLongPosts: [],

// Our observer function:
updateFemaleAuthoredLongPosts: function () {
  var self = this;
  var posts = this.get('model');
  this.set('femaleAuthoredLongPosts', []);

  posts.forEach(function(post){
    post.get('author').then(function(user){
      if( !user.get('male') && post.get('body.length') > 10000 ){
        self.get('femaleAuthoredLongPosts').pushObject(post);
      }
    });
  });
}.observes('[email protected]', '[email protected]'),

// Lastly, for some reason we also need to trigger that method in the init function.
// I can't find another way to make it fire to start.
init: function (){
  this._super();
  this.updateFemaleAuthoredLongPosts();
}

I'm going to leave this issue open as "There should be a sane way to do this."

Also that chunk of code has serious problems and doesn't always update correctly.

I haven't tried that, @IgorT. I'm actually a bit unclear what I'm looking at. I see that the store would then have a promise-accepting filter function, but then you can also set a filterFunction on any recordArray? Is that right?

It looks pretty hopeful, I don't have time to look at those failing tests, but it might be one solid solution to this issue.

(I still really like Stefan's idea for adding this to all PromiseArrays, though!)

I'm also tearing my hair out trying to do this.

My last solution with a few modifications is working for me for the moment, but I still believe it's very anti-Ember code, and needs a better solution.

I was curious to try it, and @stefanpenner's solution worked right away:

http://emberjs.jsbin.com/moguci/1/edit?html,js,output

It's just Stefan has forgotten the .filter function in his snippet. ^_^'

thanks @lolmaus

Are there any updates on this? The code above doesn't seem to work anymore in 2.1.

@lan0 Why not? What's the problem specifically?

Please reproduce the problem on http://ember-twiddle.com/ .

@lolmaus May I ask where to put $.mockjax code in ember-twiddle?

@nightire Uhm, you can't, I guess. Use https://emberjs.jsbin.com then.

@lan0 Actually I've tried the same thing at first: https://ember-twiddle.com/b52ef7dfecbd6a08c86d

But unfortunately it won't work because ember-twiddle won't allow pre-flight request??? I'm really confused...

I also have this issue. +1

Is this still an issue since references were introduced?

Is this still an issue since references were introduced?

I don't believe so.

@wecc @stefanpenner can any of you elaborate on how references solve this problem, please?

Was this page helpful?
0 / 5 - 0 ratings