Cannot get new "backgroundReload" behaviour for Ember Data 1.13 working


#1

Hi.

I am using Ember 1.13.5 and Ember Data 1.13.7.

In the blog post for the new Ember Data 1.13 it says:

Having observed many Ember apps in the wild, we have realized that neither of these two behaviors are the most common use case and deserving of being the default. The most commonly desired behavior we have seen in Ember apps is:

  • First time store.find is called, fetch new data
  • Next time return cached data
  • Fetch new data in the background and update

This is the behavior of the new findRecord and findAll methods.

However, I am not getting this behaviour in my application and I’m wondering what I am doing wrong.

My router.js is as follows:

export default Router.map(function() {
    this.route('elavonApplications', function() {
        this.route('list');
    });
    this.route('elavonApplication', { path: '/elavonApplication/:id' });
});

The “elavon-applications.js” route file looks like this:

import Ember from 'ember';

export default Ember.Route.extend({

    model: function() {

        return Ember.RSVP.hash({
            // Always get related models.
            elavonApplications: this.store.findAll('elavonApplication'),
            isoApplicationStatuses: this.store.findAll('isoApplicationStatus')
        });
    }
});

The nested “list.js” route file looks like this:

import Ember from 'ember';

export default Ember.Route.extend({
    queryParams: {
        all: {
            refreshModel: true
        },
	    highRisk: {
            refreshModel: true
	    },
	    prohibited: {
            refreshModel: true
        }
    },

    model: function(params) {
        if(Ember.isPresent(params.highRisk)) {
            return this.store.peekAll('elavonApplication').filterBy('highRisk', 1);
        } else if(Ember.isPresent(params.prohibited)) {
            return     this.store.peekAll('elavonApplication').filterBy('prohibited', 1);
        } 

        // No filter query parameters have been passed so return all application records from the store.		
        return this.store.peekAll('elavonApplication');	
    },
});

and the “elavon-application.js” route looks like this:

import Ember from 'ember';

export default Ember.Route.extend({

    model: function(params) {

        return Ember.RSVP.hash({
            elavonApplication: this.store.findRecord('elavonApplication', params.id),
            isoApplicationStatuses: this.store.findAll('isoApplicationStatus'),
            auditLog: this.store.query('log', {filter: {object: 'IsoApplication', object_id: params.id}}),
            templates: this.store.findAll('template')
        });
    },
});

So, as I see it the “elavon-applications” route is hit first and pulls in all “elavonApplications” and “isoApplicationStatuses” from the server. The “list” route is then hit and grabs the already existing “elavonApplications” either filtered or not depending upon which query params have been passed in.

In application terms this produces a list of elavonApplications on screen in a table. If one of the table rows is clicked the “elavon-application” route is fired to display an individual elavonApplication on screen.

This all works fine. However, when I click the “back” button or click the on-screen link to transition back to the elavon-application list the data reloads from the server and the UI is in a “loading” state until the promises return. As we have already loaded all the elavon-applications and iso-application-statuses when we visited this route earlier and as we are using the new find methods why is the transition not occurring immediately? I would have expected no loading state and instead for the server requests to fire in the background with the UI displaying immediately.

Can anyone tell me what I am doing wrong or whether I am misunderstanding how the new “findAll”, “findRecord” etc methods work?

Thanks.


#2

I just ran into the same thing. After looking at the source code a bit, I’ve come to the conclusion that you’re not misunderstanding how the new methods work. It’s just that the new methods don’t actually implement that new behavior yet. If you look at the current source code for the base Adapter class, shouldReloadAll still always returns true:

shouldReloadAll: function (store, snapshotRecordArray) {
  var modelName = snapshotRecordArray.type.modelName;
  Ember.deprecate('The default behavior of shouldReloadAll will change in Ember Data 2.0 to always return false when there is at least one "' + modelName + '" record in the store. If you would like to preserve the current behavior please override shouldReloadAll in your adapter:application and return true.', false, { id: 'ds.adapter.should-reload-all-default-behavior', until: '2.0.0' });
  return true;
}

I overrode this method in my ApplicationAdapter like this:

shouldReloadAll: function shouldReloadAll(store, snapshotRecordArray) {
  return snapshotRecordArray.length === 0;
}

Seems to work. I just pinged Igor Terzic on the Ember Community Slack to see if I’ve understood this correctly and if the blog post should be changed to say that “this will be the new behavior in 2.0.”


#3

Great stuff! If you could update the post when you know more I’d be grateful. Cheers!


#4

Any news on this Andrew?


#5

I have just updated to Ember 2.0 and I am still having a problem with the background reload behaviour not occurring. My transitions pause waiting for the route’s promises to resolve even though the data that is being requested is already cached in the store. Does the code above in my routes suggest any reason why the backend API is still being polled each time the route is called?

Any help would be greatly appreciated.


#6

OK, I’ve solved this one.

The problem is that Ember.RSVP.hash will only return a value when all of the promises it contains are resolved. As one of my promises was returned from a store.query() request it would not resolve until a call to the back end API was completed, as store.query() does not do a background reload like store.findAll & store.findRecord. This means that even though the other promises in the hash resolve from the store cache & do a background reload, the query call would delay the transition to the route & it would look like they did not.

The solution in my case was to load the store.query() in the “didTransition” action of the route, which occurs after the transition & therefore does not slow it down while it waits for the query promise to return. I then set the promise returned from the query to a controller value in the same action.

The result of this was that the transition to the route occurred immediately with updates occurring in the background & the “auditLog” query then loaded lazily & didn’t cause any delays.