Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model.find().then() fires before records are actually loaded

I would like to load an entire collection and then just peel off records to use as models one at a time, without doing a roundtrip to the server every time.

I've figured out how to use Ember.Deferred to return a promise, but I can't get the promise to resolve at the right time. The following code just outputs "Found 0" ever time:

App.PersonRoute = Ember.Route.extend({
  model: function(params) {
    var name = "Erik";
    var promise = Ember.Deferred.create();
    App.people = App.Person.find();

    App.people.then(function() {
      console.log('Found ' + App.people.get('length'));
      var person = App.people.findProperty('name', name)
      promise.resolve(person);
    });

    return promise;
  }
});

If I wrap the body of the then() in a setTimeout, and make it wait a couple seconds, everything works great.

Is there another event I can somehow bind to? I tried App.people.on('isLoaded'), but isLoaded is always true.

Thanks!

like image 518
Erik Pukinskis Avatar asked Jun 22 '13 21:06

Erik Pukinskis


2 Answers

Is there another event I can somehow bind to?

Indeed there is an event you can listen to and that is didLoad.

I tried App.people.on('isLoaded'), but isLoaded is always true.

As for isLoaded there was a lot of confusion about this, see here for example, the confusion comes from the fact that the isLoaded flag is set to true by design when the store has finished loading the RecordArray for the records, even when initially empty because no record was already available locally. Then when the request to the server comes back the RecordArray will be populated with the records received from the backend, and bindings will kick off and your templates are updated.

As stated in the guides:

A record that is both loaded and clean means that is has received information about its attributes and relationships from the server, and no changes have been made locally on the client.

Was is stated above is what makes didLoad fire.

For more model related events you can listen to have a look at the guides under model lifecycle

Now to your setup, you could rewrite your code to something like this:

App.PersonRoute = Ember.Route.extend({
  model: function(params) {
    var name = "Erik";
    var promise = Ember.Deferred.create();
    App.people = App.Person.find();

    App.people.on('didLoad', function() {
      console.log('Found ' + App.people.get('length'));
      var person = App.people.findProperty('name', name)
      promise.resolve(person);
    });

    return promise;
  }
});

Hope it helps.

like image 104
intuitivepixel Avatar answered Nov 11 '22 20:11

intuitivepixel


If the promise isn't resolving at the right time, I think it would be a (fairly major)bug in Ember/data. I would suggest filling a bug with Emberjs/data.

I suspect though that your use of the different promise may be causing this bug.

App.Person.find is already returning a promise. You should use that promise itself when returning from the model(). Also to resolve that promise you simply return a result.

The implementation style for Promises that you have used is typically needed when you are integrating some external async api that doesn't support Promises.

I would refactor your model() like so. That might fix your async timing issue.

App.PersonRoute = Ember.Route.extend({
  model: function(params) {
    var name = "Erik";
    var promise =  App.Person.find();

    promise.then(function() {
      console.log('Found ' + App.people.get('length'));
      return App.people.findProperty('name', name)
    });

    return promise;
  }
});

I've found the docs on the Q library to be very useful when figuring out best way to use promises. Ember uses RSVP which is a different library but the principles are similar.

like image 34
Darshan Sawardekar Avatar answered Nov 11 '22 19:11

Darshan Sawardekar