Force store to reload data from API


#1

I want to re-query for new data on the backend every time someone clicks a link for a nested resource. At first I thought this was going to be a simple process but ember doesn’t seem to like hitting the database to collect information on a model that is already in store.

Say for a example I have some list of links that point to some posts and when a use clicks on those posts I want detailed data for each one of those posts to be displayed to the user. When the user view goes to the page this list is on, JSON is served up that looks a lot like this

{
  posts:[
    {
      id: '1',
      title: 'Post A'
    },
    {
      id: '1',
      title: 'Post A'
    },
    {
      id: '2',
      title: 'Post B'
    },
    {
      id: '3',
      title: 'Post C'
    }, 
  ]
}

My posts route is setup to execute a findAll and load up a bunch of posts models for me

App.Router.map ()->
  @resource 'posts', ->
    @resource 'post', { path: ':post_id' }

App.PostsRoute = Ember.Route.extend
  model: ->
    @store.findAll('posts')

App.PostRoute = Ember.Route.extend
  model (params): ->
    @store.find('post', params.post_id)

The list renders out fine. The template contains an outlet for a PostView to render into so that user can click from post to post and have the details of each post displayed to him on the same page as the post list.

The trick is getting post date like Author, Content, Comments into these Post models that I have loaded into the store. I thought that when a user clicked on the #/posts/1 link the PostView would render into the outlet I have setup but that is not happening. It seems that @store.find just sees that there is a post with the given ID already in the store and just serves that up. How can I force him to query for the detailed data for a post stored at /posts/:id?

If it helps anyone. I can commit a small rails/ember project on github containing my example.

The resourced in these links:
http://emberjs.com/guides/routing/query-params/#toc_opting-into-a-full-transition


helped me some but I still cannot get the desired behavior.

TLDR; @store.find doesnt re-query the backend API when a model already exists in store w/given ID.


#2

@bemathis I would try 1) calling reset() on the controller instance or 2) calling refresh() on the router instance.

Perhaps this question should be posted in stackoverflow with the ember.js tag.


#3

Hi @pixelhandler. Your solution probably would have worked for me had queryParamsDidChange not been broken. However, I was able to get the desired behavior by wrapping each link to a post in an action called getPostDetails like so

{{#link-to 'registry' this.id}}
  <span {{action 'getRegistryDetails' this}}>
    {{name}}
  </span>
{{/link-to}}

Now I just call model.reload() in that action defined in the posts route and it obtains the data that I want from the backend. Pretty simple, everything works great!


#4

The answer you got is right. By default ember doesn’t assume that he should fetch again a model already loaded. Nothing makes ember-data think that the model has changed, which is a reasonable default.

If you KNOW that a given kind a model changes often and want to fetch it again, you can force a reload, but I would like to point out some things:

  1. For your last answer I had a bad feeling. Seems that when you load posts for the index action your API is returning only a subset of the post’s information and then you need to perform a full fetch in order to the ALL the data. If this is the case, probably you are facing the problem wrong. Ember data follows JSON API guidelines. One, and probably the most important rule of JSON API is that a model should not get different data if asking from different endpoints.

A call to /api/posts returns all posts. A call to /api/posts/123 returns a single post. But in both cases, the posts must have all the information. You can still work against that guideline, but you will need to fight a bit against the framework. Nothing too hard as you have seen, but still makes you think if you should face the problem in a different way.

I had the same problem earlier. I did not want to send all the information in the index for backend performance reasons. Sometimes because is a complex operation, other times because you have a model that has some information that never changes and a subset of information that changes a lot and that was f***ing with your cache expiration policy.

For me the answer was to split the model into 2 endpoints. In this case could be /api/posts and /api/post_bodies/ that returns something like this:

{
  posts: [
    {id: 1, title: "some title"}
    {id: 2, title: "another title"}
  ]
}
{
  post_bodies: [
    {id: 1, text: "Lorem ...", post_id: 1},
    {id: 2, text: "Dolor sit...", post_id: 2},
  ]
}

Might seem overkill, but helped me a lot when is completely impossible to send all the information in the index action for performance reasons.

  1. If I got it wrong and you want to reload the post just because it might have changed, this is a clever trick that might be handy to not wait for the response and start displaying the view right away:
App.PostRoute = Ember.Route.extend({
  model: function(id){
    var post = this.get('store').find('post', id); // Find the post from the store
    post.reload(); // Force a reload
    return post;  // Return the fetched post NOW with incomplete/outdated info. When the api answers, the information will be updated.
  }
});

Hope it helps.


#5

That is an excellent point that you make about the JSON api loading identical data for /posts and /posts/1 but my issue comes into play for loading object associated to posts like post comments. I can’t be loading all the comments for every post every time I just want to see all the posts. I would like:

/posts to load in an index list of all posts and all details you need about that post

/posts/1 load all the data from above about the post + comments and any other data associated with that post


#7

I have a similar problem, however in my case several users are updating the same data. I want to get fresh data not in the list view but the detail view.

Thus, I disagree that the data model should have such a strong influence on the domain. I think the cleanest approach would be to treat Ember Data as thought it were a CDN as it has the same use case. If Ember Data was to be modified to respect cache headers then this is an easier problem. Some data can be cached “forever” and other data is not cacheable–it’s up to the application to define this.

There are only 2 hard problems in software, naming things, cache invalidation, and off by one errors. I think in this case it’s best to copy the CDN approach since it is already so successful.


#8

I’d like to resurrect this thread to see if you guys have had any revelations in the interim. I’m working with a system where the data is constantly changing, and there are too many points of entry/data points for something like sockets to be viable. Basically, I’d like to use ember-data but always query the backend for every data request. I’m surprised there’s no way to simply disable caching on ember-data (or is there?). Cheers for any enlightenment.


#9

You can call model.refresh() on any route, that works for me for most issues with often-updating data.


#10

I’ve been manually reloading as well. Just surprised there’s no global “off switch”.


#11

I agree, it would be super nice if you could tell it to “always” make an API call in certain cases.


#12

The latest version of Ember Data 1.0.0-beta.12 now has a store.fetch method to solve this exact use case. http://emberjs.com/api/data/classes/DS.Store.html#method_fetch


#13

I faced a similar problem recently and found out that for Ember 1.11.1 two things are important:

  • In the route model, return this.store.fetchById function which forcefully reloads the model triggering a new request
  • When creating a link to the detail view, make sure to pass only the id instead of the whole model to link-to helper as follows: {{#link-to 'users.edit' user.id}} instead of {{#link-to 'users.edit' user}}

#14

Update - getById while including the id in {{#link-to ‘users.edit’ user.id}} will make a fresh api call. fetchById is deprecated.


#15

I don’t quite understand this thought. What if you’re building a CMS backend that could be used by multiple users? It seems like the only way the user could see content created/edited by other people is if;

a. They did a hard refresh, which is very counterintuitive. Or,

b. You implement WebSockets alerts for every single data change.

Have I got this wrong?


#16

Route has a property named ‘currentModel’. Not sure if it’s supposed to be public though.

App.PostRoute = Ember.Route.extend
  model: (params,transition)->
    if @currentModel && @currentModel.get('id') == params.id
      @currentModel.reload()
    else
      @store.find('post',params.id)

#17

Thought i might drop this here, as there have been no updates since June 18th…

If anyone is wondering how to approach this in Ember 2.0 (in particular with the post-Ember Data 1.13 API update), I wrote about it:

http://emberigniter.com/force-store-reload-data-api-backend/


#18

Since this thread never reached a conclusion in newer Ember you can use the shouldReloadAll method to disable the caching.