belongsTo data of the model isn't loaded


#1

Here is my model: loan.js

import DS from 'ember-data';
export default DS.Model.extend({
  notes: DS.attr('string', {defaultValue: ''}),
  returned: DS.attr('boolean'),
  createdAt: DS.attr('date'),
  friend: DS.belongsTo('friend'),
  article: DS.belongsTo('article')
});

This is my model for article:

import DS from 'ember-data';
import { hasMany } from 'ember-data/relationships';

export default DS.Model.extend({
  name: DS.attr('string'),
  loans: hasMany('loan')
});

For my route loans/index this is what I have

import Ember from 'ember';

export default Ember.Route.extend({
  model(){
    return this.modelFor('friends.show').get('loans');
  }
});

Finally my loans/index.hbs is as such

<tbody>
    {{#each model as |loan|}}
    <tr>
      <td>
        {{log loan}}
        {{loan.article.name}}
      </td>
      <td>
        {{loan.notes}}
      </td>
      <td>
        {{loan.createdAt}}
      </td>
    </tr>
    {{/each}}
  </tbody>

Finally the response from the API

{

    "data": [
        {
            "id": "1",
            "type": "loans",
            "links": {
                "self": "http://ember-cli-devinda.herokuapp.com/loans/1"
            },
            "attributes": {
                "notes": "fgfhf",
                "returned": false,
                "created-at": "2017-08-08T11:22:25.136Z"
            },
            "relationships": {
                "article": {
                    "links": {
                        "self": "http://ember-cli-devinda.herokuapp.com/loans/1/relationships/article",
                        "related": "http://ember-cli-devinda.herokuapp.com/loans/1/article"
                    }
                },
                "friend": {
                    "links": {
                        "self": "http://ember-cli-devinda.herokuapp.com/loans/1/relationships/friend",
                        "related": "http://ember-cli-devinda.herokuapp.com/loans/1/friend"
                    }
                }
            }
        }
    ],
    "links": {
        "first": "http://ember-cli-devinda.herokuapp.com/friends/1/loans?page%5Bnumber%5D=1&page%5Bsize%5D=10",
        "last": "http://ember-cli-devinda.herokuapp.com/friends/1/loans?page%5Bnumber%5D=1&page%5Bsize%5D=10"
    }

}

When i include {{loan.article.name}} the console only prints out ‘loan’ once. This is my result when I convert loan to json with .toJSON()

If i take the ‘loan’ object, save it in the console (as temp0) and run temp0.get(‘article’).get(‘name’) it returns undefined. However if I run the very same code again in the console I get the desired result (the name of the article which belongs to said loan)!

I figured this might mean that when the page is loaded, the model (loan) doesn’t load its belongsTo(‘article’) data.

Thanks !


#2

Hi @dca123, I’ll take a stab at what I think is happening here:

It sounds to me like when you fetch a “loans” document from the API, it’s NOT including the data for the related models. Not sure if this is intentional or not, but the API response has no data in the relationships on article/friend so no data will be loaded for those models. The behavior you’re seeing in the console can be explained because the relationships are async. Basically the first time you try and fetch it the data isn’t there, or at least isn’t all there (kinda seems like Ember Data may have half created a record based on the relationships hash included in the response but since there isn’t a record in there it can’t fully created it) so it returns undefined. Then in the background it does an async update/fetch to get the related model. So then by the time you do the second .get the related model is there as expected.

So to fix this, you’ll either want to actually include the related records in the payload on the payload (under data.relationships.article.data for example), like in the JSON API docs, or maybe look over the JSON API specs and make sure that including relationship links without the relationship data is what you’re supposed to do to support async relationship fetching correctly. I wish I could be more help there but I’m not all that up on the JSON API spec. Put differently, if you’re actually trying to sideload the data, it needs to be included in the response. If not, be aware that async data fetching will occur on the first reference to the relationship, however I’d expect it to return a promise and not undefined so maybe there’s something going on with your API response that creates a “partial relationship” somehow that you should remove.


#3

Thanks for your response ! I reviewed the JSON API requirements and it says that it MUST have either of a links or data object inside the relationship object. I just understood that an async relationship is one where ember tries to resolve the data in the relationship when it is first loaded. What I noticed what that when I first load the page the only makes a request to ‘friends/:friend_id’ and ‘friends/:friend_id/loans’ and doesn’t resolve one to ‘loans/:loan_id/article’.

However when i type in the console temp0.get(‘article’).get(‘name’), it does make a request to ‘loans/:loan_id/article’ and returns undefined. On the second attempt it doesn’t make the request since the data has been loaded and it merely displays the name of the article.

My question is why do you think that ember-data isnt’t making a request for ‘loans/:loan_id/article’ when I’ve explicitly stated that async is true in the models ?

Edit: Ok so I solved this by changing the routes file for /loans/index.js to the following

model() {
return this.modelFor('friends/show').get('loans').then(function(loans) {
  return loans.forEach((loan) => {
    loan.get('article');
  });
});

}

This forces ember to load the related article to the given loan. However this causes the page loading to be delayed till all the articles are loaded.

Might there be a more effective way in which I can achieve this ?

Thanks !