Computed Properties not working with server data


#1

I can’t figure out why my computed properties aren’t working, but it seems to have something to do with fetching the data from the server. When the app fetches the data from the server, the regular property (days) will be displayed properly (see the view below), but the computed property (dates) produces two empty <li>s. However, when I insert the data through this.store.push in the route object’s model function (so the data is on the client before the view is rendered), then both properties are properly displayed?

How can I get the computed property to re-compute after the hasMany relationship models are fetched from the server?

The Calendar Model:

import DS from 'ember-data';
import Ember from 'ember';

export default DS.Model.extend({
    name: DS.attr('string'),
    days: DS.hasMany('day'),
    dates: Ember.computed('days.@each', function () {
        return this.get('days').map(day => day.get('date'));
    })
});

The Day Model

import DS from 'ember-data';

export default DS.Model.extend({
    date: DS.attr('string'),
    calendar: DS.belongsTo('calendar')
});

The view

The "regular" property
<ul>
{{#each model.days as |d|}}
    <li>{{d.date}}</li>
{{/each}}
</ul>

The computed property
<ul>
{{#each model.dates as |d|}}
    <li>{{d}}</li>
{{/each}}
</ul>

The data in the route

import Ember from 'ember';

export default Ember.Route.extend({
model() {
this.store.push({
  "data": [{
    "type":"calendar",
    "id":1,
    "attributes": {
      "id":1,
      "name":"first calendar",
      "days":[2,3],
    },
    "relationships": {
      "days": {
        "data":[
          {"id":2,"type":"day"},
          {"id":3,"type":"day"}
        ]
      }
    }
  }, {
    "type":"day",
    "id":2,
    "attributes":{
      "id":2,
      "calendar":1,
      "date":"2016-06-15"
    }
  }, {
    "type":"day",
    "id":3,
    "attributes":{
      "id":3,
      "calendar":1,
      "date":"2016-06-17"
    }
  }]
});

return this.store.findRecord('calendar', 1);
}
});

#2

Hello,

this.get('days') return a promise (https://guides.emberjs.com/v2.5.0/models/relationships/#toc_relationships-as-promises).

I think this code should work. For your Calendar Model (see http://emberjs.com/api/classes/Ember.ArrayProxy.html and http://emberjs.com/api/classes/Ember.PromiseProxyMixin.html):

export default DS.Model.extend({
    name: DS.attr('string'),
    days: DS.hasMany('day'),
    dates: Ember.computed('days.@each', function () {
        const proxy = Ember.ArrayProxy.extend(Ember.PromiseProxyMixin);
        
        return proxy.create({
            promise: this.get('days').then(days => { return days.map(day => day.get('date')); })
        });
    })
});

And in your view:

The computed property
{{#if model.dates.isFulfilled}}
    <ul>
        {{#each model.dates as |d|}}
            <li>{{d}}</li>
        {{/each}}
    </ul>
{{else}}
    <p>waiting dates...</p>
{{/if}}

#3

If you use Ember Data, i think you can use DS.PromiseArray (instead of Ember.ArrayProxy + Ember.PromiseProxyMixin).


#4

Thanks! That’s great!