2

Embsters!

I am trying to figure out why my model isn't refreshed after I create a new record and save it to the store.

My route computes the model as follows:

model: function (params) {
  var postID = params.post_id,
      userID = this.get('session.currentUser.id');

  var post = this.store.findRecord('post', postID) ;

  var followings = this.store.query('post-following', {
      filter: { post: postID }
  }) ;
  var userFollowing = this.store.queryRecord('post-following', {
      filter: { post: postID, user: userID }
  }) ;

  return new Ember.RSVP.hash({
      post:          post,
      followings:    followings,
      userFollowing: userFollowing
  });
}

My template then renders a list and a button:

{{#each model.followings as |following|}}
    ...
{{/each}}

{{#if model.userFollowing}}
    <button {{action 'follow'}}>Follow</button>
{{else}}
    <button {{action 'unFollow'}}>Unfollow</button>
{{/if}}

And my controller creates/deletes the relevant post-following record:

actions: {
    follow: function () {
        var user = this.get('session.currentUser'),
            post = this.get('model.post') ;

        this.store.createRecord('post-following', {
            user: user,
            post: post
        }).save();
    },
    unFollow: function () {
        this.get('model.userFollowing').destroyRecord() ;
    }
}  

When I click the [Follow] button:

  • a successful POST request is sent
  • the button is not updated
  • the list is not updated

When I (refresh the page then) click the [Unfollow] button:

  • a successful DELETE request is sent
  • the button is not updated
  • the list is updated

Do you have any idea of what I'm doing wrong?
Could it be a problem with my payload?


EDIT: Solved!

Well, it sounds like I was expecting too much from ember.

The framework won't automatically update my post-followings array on store.createRecord('post-following', {...}) call.

I then adjusted my controller logic to "manually" update my model:

// in follow action…
userFollowing.save().then( function(){
    var followings = store.query('post-following', {
        filter: { post: postID }
    }).then( function (followings) {
        _this.set('model.userFollowing', userFollowing);
        _this.set('model.followings', followings);
    }) ;
});
// in unFollow action…
userFollowing.destroyRecord().then( function () {
    _this.set('model.userFollowing', null);
    _this.notifyPropertyChange('model.followings') ;
}); 

Please note that my backend API design has been criticized by @duizendnegen (see comments). More best practices in this article.

Thanks you for all your help !!!
Brou

Brou
  • 90
  • 8

3 Answers3

1

Your issue is that you aren't re-setting the property model to point to the newly created object. You are always accessing the same model property, even after creating a new one.

First thing to be aware of is that, after the model hook in your route, the setupController hook is called that executes:

controller.set('model', resolvedModel)

meaning that the model property on your controller is, by default, set every time the route loads (the model hook resolves). However, this doesn't happen after you create a new record so you must do it explicitly:

let newModel = this.store.createRecord('post-following', {
  user: user,
  post: post
})

// since model.save() returns a promise
// we wait for a successfull save before
// re-setting the `model` property
newModel.save().then(() => {
  this.set('model', newModel);
});

For a more clear design, I would also recommend that you create an alias to the model property that more specifically describes your model or override the default behavior of setupController if you are also doing some initial setup on the controller. So either:

export default Ember.Controller.extend({
  // ...

  blog: Ember.computed.alias('model') // more descriptive model name   

  // ...
});

Or:

export default Ember.Route.extend({
  // ...

  setupController(controller, resolvedModel) {
    controller.set('blog', resolvedModel); // more descriptive model name 
    // do other setup
  }

  // ...
});
nem035
  • 34,790
  • 6
  • 87
  • 99
  • 1
    Thank you for your tips and help! As mentioned to @duizendnegen, setting the model "manually" after my request was my previous approach. It visually worked, but raised a strange empty `{}` error when trying to set my `model.followings` property. I also felt like ember should be handling that refresh alone… Is it actually the good way to go? – Brou Apr 04 '16 at 16:56
  • 2
    Well, the code you show expects to display a model that you **create sometime later**. Thus it cannot know automatically that you did this unless you tell it explicitly. [Here's a good read](http://emberigniter.com/force-store-reload-data-api-backend/) that shows how ember-data caches the models by default and how to make it always reload. – nem035 Apr 04 '16 at 17:15
1

For these kind of questions, it really helps to have a smaller, replicated problem (e.g. through Ember Twiddle)

Fundamentally, the new post-following record doesn't match the filter: it is filtered for an attribute { post: 123 } and your post-following object contains something in the lines of { post: { id: 123, name: "" } }. Moreover, your post-following object doesn't contain a property called filter or what it could be - i.e. the query it executes to the server are different than those you want to filter by on the client.

My approach here would be to, as a response to the follow and unfollow actions, update the model, both the userFollowing and followings.

  • Thanks a lot for your help! **Fiddle:** I tried, but it sounds impossible to call `store.query` using a FixtureAdapter… // **Model:** My backend filtering logic is: `GET /postfollowings?filter[post]=123`. The `filter[…]` syntax gives a cleaner API separation between filtering, sorting, and metadata QS params. Could it mess with ember? // **Your approach:** That's what I was doing before. It visually worked, but raised a strange empty `{}` error when trying to set my `model.followings` property. I also felt like ember should be handling that refresh alone… Is it actually the good way to go? – Brou Apr 04 '16 at 16:49
0

Your model is set when you enter the page. When changes are made, your model doesn't change. The only reason why the list is updated when you destroy the record is because it simply doesn't exist anymore. Reload the model after clicking the follow button or unfollow button, or manually change the values for the list/button.

Jevado
  • 392
  • 1
  • 6
  • Thank you for your answer! **I make ~10 requests in my model hook though**, so it doesn't feel really good to trigger a full model refresh. **Do you know how to trigger a *sub-model* refresh?** (i.e. `model.followings` and `model.userFollowing` only) – Brou Apr 04 '16 at 16:48
  • 1
    That is indeed possible in the way your model is constructed. You can use something like: this.store.query().then(result => { this.get ('currentModel').set('followings', result); }) – Jevado Apr 04 '16 at 17:08