Asked By: Anonymous
I have a simple hasMany/belongsTo relationship that looks like this
App.Foo = DS.Model.extend({
bar: belongsTo('bar', { async: true})
});
App.Bar = DS.Model.extend({
foos: hasMany('foo', { async: true})
});
I have situations in my route code that fire off a request and when the response comes back I access the related “bar” model as I filter down / etc
this.store.all('foo').clear();
var fooz = self.store.all('foo'); //new code
return this.store.find('foo').then(function(response) {
var filtered = response.filter(function(foo) {
return foo.get('bar').get('name') === 'bazz';
});
//other code that would normally be executed top-down
//including side-effect stuff like this
//self.store.createRecord('foo', someHash);
return fooz; //new code
});
The above doesn’t work the first time around as foo.get(‘bar’) is a promise. But this is only a problem the first time through (subsequent $.ajax requests seems to have all the bar objects cached so it’s a non issue)
What’s strange is that before I even boot the app I’ve already pulled down all the bar data in the init (shown below). So why does ember-data even need to resolve the promise for “bar” when technically that data should already be in the store locally?
App.initializer({
name: 'bootstrap',
initialize: function() {
App.deferReadiness();
var store = App.__container__.lookup("store:main");
var bars = store.find('bar');
var configurations = store.find('configuration');
Ember.RSVP.all([bars, configurations]).then(results) {
App.advanceReadiness();
});
}
});
Solution
Answered By: Anonymous
Let’s separate a few things here
Store cache
this.store.all('foo').clear();
just breaks the internal all
filter until a foo
record is modified/added/removed forcing the filter to recalculate for the record in the store. I say this to show that clear isn’t removing the records from ED’s store.
Example (click the button, watch the console, read the fun action code)
http://emberjs.jsbin.com/OxIDiVU/103/edit
That being said it isn’t the ajax that’s being cached, it’s the property/relationship on the record instance that’s being cached (and the record).
The proper way to remove the records of a type from the store is store.unloadAll('foo')
Promiselandia
I know you’re already familiar with promises, so this part may be worthless, but, worth documenting
The async relationships are really cool because they return PromiseObject
/PromiseArray
for belongsTo
/hasMany
. The PromiseObject
/PromiseArray
extend ObjectProxy
/ArrayProxy
(these are the same things that ObjectController
/ArrayController
extend). This essentially gives the PromiseObject
/PromiseArray
the ability to proxy getting/setting of properties to the model underneath. In this case the setting/getting happens on the promise doesn’t “work” until the promise has been resolved (it won’t crash, just return undefined). *Caveat, methods don’t exist on the promise, so you can’t call save on the promise and expect it to work.
Ex. using your models.
var foo = this.store.find('foo', 1);
var bar = foo.get('bar'); // PromiseObject
bar.get('name'); // undefined
later, bar has resolved, bar is still the PromiseObject
bar.get('name'); // billy
foo will keep returning the PromiseObject
var bar2 = foo.get('bar'); // PromiseObject
bar2.get('name'); // billy
saving
bar.save(); // Boom no workey
bar.then(function(realBar){
realBar.save(); // workey
});
In your case I have 3 recommendations
Build your own promise, resolve when you want, use Ember.RSVP.all on the records needed (respecting that they may or may not be resolved yet, hence async)
var self = this;
var promise = new Ember.RSVP.Promise(function(resolve, reject){
self.store.find('foo').then(function(foos) {
Em.RSVP.all(foos.getEach('bar')).then(function(bars){
var filtered = bars.filterBy('name', 'bazz');
resolve(filtered);
});
});
});
return promise;
http://emberjs.jsbin.com/OxIDiVU/104/edit
async properties
A lot of times with async objects/properties that aren’t resolved during the model hook (which blocks on promises and wait’s until they are resolved) a good trick is to set a placeholder object etc.
var items = [];
controller.set('model', items);
// promise from above
promise.then(function(records){
items.pushObjects(records.toArray()); // toArray may or may not apply
});