1

Let's say there is a route with capabilities to update it's data when requested by the user (assume the backend returns different data for the same call, maybe it's stock data, or just random numbers).

export default Ember.Route.extend({
  model() {
    return this.get('store').findAll('foo');
  },

  actions: {
    invalidateModel() {
      this.refresh();
    }
  }
});

Now, a component consuming this model directly will update its view as expected.

Model: {{#each model as |m|}}{{m.bar}}{{/each}}
<button {{action "refreshModel"}}>Refresh model</button>

But, if the component is using a computed property observing the model, then the updates do not carry through.

Template

Model: {{#each computedModel as |m|}}{{m}}{{/each}}
<br>
<button {{action "refreshModel"}}>Refresh model</button>

Component

computedModel: Ember.computed('model', function() {
  return this.get('model').map(function(m) {
    return `Computed: ${m.data.bar}`;
  });
}),

For a full repro, you can check out: https://github.com/myartsev/ember-computed-properties-on-data-model

The latest commit is the non-working computed properties case.
The previous commit is when everything is still working correctly when using the model directly.

What am I missing?

myartsev
  • 1,207
  • 2
  • 13
  • 23

2 Answers2

4

Your computed property is listening for changes to the array itself. Try listening for changes to the arrays items with model.[]

https://guides.emberjs.com/v2.15.0/object-model/computed-properties-and-aggregate-data/#toc_code-code-vs-code-each-code

computedModel: Ember.computed('model.[]', function() {
  return this.get('model').map(function(m) {
    return `Computed: ${m.data.bar}`;
  });
}),

UPDATE

Here is a twiddle showing that the above solution fixes the problem.

If it's not working on your end then there is some issue with what your api is returning.

As per my comments about how to send actions. You are using 2 year old syntax from Ember 1.13 which I am not familiar with.

I suggest you read the docs for the version you are running Ember 2.15

Subtletree
  • 3,169
  • 2
  • 18
  • 25
  • Also you should join the ember slack if you haven't already. https://ember-community-slackin.herokuapp.com/ : ) – Subtletree Sep 14 '17 at 05:51
  • Did you try this? It doesn't work :( The ember data model is a class, not an array, at least not directly (it does have arrays inside the class) – myartsev Sep 14 '17 at 05:56
  • You're not sending the action correctly to the component, you're just sending a string. Try `{{foo-component model=model invalidateModel=(action "invalidateModel")}}` – Subtletree Sep 14 '17 at 06:25
  • Also you can't send route actions to the template by default, only actions from the controller or component. You could use the `ember-route-action-helper` addon to send actions from the route – Subtletree Sep 14 '17 at 06:27
  • The action from the component is firing just fine in the route, please read the question and try running the example. Actions up, data down. – myartsev Sep 14 '17 at 07:25
  • Updated my answer above – Subtletree Sep 14 '17 at 22:22
  • Your updated answer is using a mirage backend which directly returns an array. This is not a JSONAPI response, the default data structure for Ember Data models. – myartsev Sep 15 '17 at 01:51
  • No it doesn't directly return an array. Mirage simulates a backend response exactly. Put `{{model}}` in the template. You can see it returns a DS.RecordArray which is the correct response from ember data – Subtletree Sep 15 '17 at 01:54
  • My bad, you are right, I am not familiar with Mirage. Your answer got me thinking on the right track, but you set up a different scenario where the length of the returned data model changes each time refresh is called, which is why 'model.[]' is working for you. In my scenario, a refresh updates values in the existing data model, but does not change the length of the returned data. So the correct way was to listen to the dependent key 'model.@each.bar'. You can take a look at the latest commit in the github repo I referenced. If you'd like to update your answer I will accept it. Thank you! :) – myartsev Sep 16 '17 at 05:46
3
computedModel: Ember.computed('model.@each.bar', function() {
  return this.get('model').map(function(m) {
    return `Computed: ${m.data.bar}`
  });
})

To close the loop; the answer from @Subtletree was very close and it got me thinking on the right track.

The difference was subtle but important enough, model.[] will only work if the size of the data being returned changes; elements are added or removed. In my case, the size of the data returned remained constant, only it's value got updated. So the correct way was to listen to the dependent key you are looking for, in this case, 'model.@each.bar'.

myartsev
  • 1,207
  • 2
  • 13
  • 23