Search code examples
ember.jsember-dataember-cli

Why can't I display the second association in a nested each loop?


I have 2 models deliverHour and day. In an .hbs file, I have two nested each loops.

I am trying to display the delivery hours for each day. Apparently, once it reaches the second each loop, it doesnt render anything. Not even this line does not render!

Looking in Ember Inspector. I do not see any failed promises. The network tab doesn't even show Ember trying to attempt calling the delivery hour end point. The Data tab shows delivery-hour (0), which means no delivery hours in the store, which is strange.

Since the relationship for the day model has DS.hasMany('deliveryHour', { async: true }) I would have expected Ember to do an asynchronous call to the delivery hour api resource. No?

What am I missing here?

// Route
import Ember from 'ember';

export default Ember.Route.extend({
  model: function(params) {
    var addressId = this.checkoutCart.get('addressId');

    return Ember.RSVP.hash({
      address:       this.store.find('address', addressId),
      delivery:      this.store.createRecord('delivery'),
      days:          this.store.find('day')
    });
  },

  // You can use model.delivery to get delivery, etc
  // Since the model is a plain object you can just use setProperties
  // http://stackoverflow.com/questions/16463958/how-to-use-multiple-models-with-a-single-route-in-emberjs-ember-data
  setupController: function(controller, model) {
    controller.setProperties(model);
  },
});

// app/models/delivery-hour.js
import DS from 'ember-data';

export default DS.Model.extend({
  day:    DS.belongsTo('day', { async: true }),
  from:   DS.belongsTo('hour', { async: true }),
  to:     DS.belongsTo('hour', { async: true }),
  status: DS.attr('string')
});

// app/models/day.js
import DS from 'ember-data';

export default DS.Model.extend({
  deliveryHours: DS.hasMany('deliveryHour', { async: true }),
  name:          DS.attr('string'),
  dayOfTheWeek:  DS.attr('number')
});

// hbs
{{#each day in days}}
  <li>
    {{day.name}} // Returns something like 'Monday'
    {{day.deliveryHours}} // Returns something like <(subclass of DS.PromiseArray):ember770>
    {{day.deliveryHours.length}} // Returns 0

    {{#each deliveryHour in day.deliveryHours}}
      this line does not render!
      {{deliveryHour.hour}} <----- ideally, this is what i want to see
    {{/each}}
  </li>
{{/each}}

{{day.deliveryHours.length}} returns 0. I wanted to verify this. I am using Rails as my API. In Rails console, when I do Day.first.delivery_hours.count, it returns 4. Strange that Ember returns 0. The Network tab, in Ember Inspector, does not show Ember trying to call the delivery hours API endpoint.

On the top right corner of the screenshot, I click on the > $E. Then I go to the console tab of Ember inspector and paste: $E.get('deliveryHours'). I get Object { content: Getter, toString: makeToString/<(), isFulfilled: true, 4 more… }.

In the console of Ember Inspector, I also tried:

>> $E.get('deliveryHours').then(function(hours) { console.log(hours.get('length'));});
Object { _id: 156, _label: undefined, _state: undefined, _result: undefined, _subscribers: Array[0] }

enter image description here

Update: My API response for days returns as:

{
    "days": 
[
{

    "id": 1,
    "name": "Monday",
    "day_of_the_week": 1

},
{

    "id": 2,
    "name": "Tuesday",
    "day_of_the_week": 2

},
{

    "id": 3,
    "name": "Wednesday",
    "day_of_the_week": 3

},
{

    "id": 4,
    "name": "Thursday",
    "day_of_the_week": 4

},
{

    "id": 5,
    "name": "Friday",
    "day_of_the_week": 5

},
{

    "id": 6,
    "name": "Saturday",
    "day_of_the_week": 6
},
{
    "id": 7,
    "name": "Sunday",
    "day_of_the_week": 7
}
]
}

http://emberjs.com/guides/models/the-rest-adapter/#toc_relationships indicates that I should probably load the delivery_hours as an array of ID's. I've tried this too, same results..


Solution

  • That's because deliveryHours is an async property in your model, which is why {{day.deliverHours}} returns an unresolved promise, instead of the record array you were expecting.

    You'll have to explicitly resolve it in an afterModel hook, like so:

    // Route
    import Ember from 'ember';
    
    var RSVP = Ember.RSVP;
    
    export default Ember.Route.extend({
      // ...
    
      afterModel: function(model) {
        return RSVP.all(
          model.days.map(function(day) {
            return day.get('deliveryHours');
          })
        );
      }
    
    });