Weird loading behaviour with multiple relationships


#1

I am having a one to many relationship, which looks like this:

models/group.js:

export default DS.Model.extend({
  members: DS.hasMany('member', {
     async: true
   })
})

models/member.js:

export default DS.Model.extend({
  group: DS.belongsTo({
    inverse: 'members'
  })
})

Each model has a dedicated resource server side and is loaded separately. This has always worked well so far. However, I was trying to find a way to easily figure out which of these resources have been updated since the last visit. To get that information there already is an event resource available, which contains notifications about the updated entity.

So I added a relationship to the event model:

models/event.js:

export default DS.Model.extend({
  group: DS.belongsTo({ async: true }),
  member: DS.belongsTo({ async: true }),
})

The server sends the group id or the member id within the group or member field. The member and group model respectively contain a relationship to the events, so I just need to check if there are any events attached to the model to find out if and which updates have happened:

models/member.js:

export default DS.Model.extend({
  group: DS.belongsTo({
    inverse: 'members'
  }),
  events: DS.hasMany('event')
})

If the member now has an event attached to what happens is the following: The event is loaded before the group and members are loaded. Ember somehow creates a new member record which just contains the id of the member and the events relationship. All other values are default values. Ember then thinks it already has loaded this entity and even when navigating to the route the member never gets loaded from the server. Is there anything I can do to make this work in a clean way or is this a sort of a bug somehow?

ember data is 2.11.3


#2

member should be:

export default DS.Model.extend({
   group: DS.belongsTo('group', { inverse: 'members'}),
   events: DS.hasMany('event')
})

event should be:

export default DS.Model.extend({
   group: DS.belongsTo('group', { async: true }),
   member: DS.belongsTo('member', { async: true }),
})

You may refer Relationships section in Ember Guides for better understanding.


#3

Thank you for the reply. Did I get it right that I was just missing the string describing the entity as the first argument of the belongsTo method? I think I missed correcting that when I was porting the app from an ember data alternative. I changed that, however the issue persists. However, if I enter the application directly via the specific group url, the group gets loaded before the events and everything works well (the members are displayed in the group template, so they will be loaded correctly as well). If I enter the app via the root url, the events get loaded before I navigate to a group and the member is never actually loaded. Yet, ember data creates a pretty much uninitialised members entities, which its thinks has been loaded from the server correctly. It is neither dirty nor new and has isFulfilled set to true.

I have looked at the documentation about relationships a few times, however I have not found anything describing the behaviour I am seeing. I could load the member or group programmatically after receiving the event, however they will be loaded anyway if the user accesses the template displaying the entity (unless ember data does create a fake entity before that is the case, which seems to be the actual issue I am having - but I could be probably be wrong about that either way…)

If I set a breakpoint in the model hook of the member route and save the output of the promise to a variable:

var m;
this.store.findRecord('member', params.member).then(function(member) { m = member});

and then I can do:

m.get('isLoaded')
//true
m.get('title')
//undefined -- this would have a value set, if it had been requested from the server
m.get('id')
//"1" -- ok
m.get('events.isFulfilled')
//true
m.get('events.length')
//3 -- ok
m.get('group.name')
//"group name" -- ok
m.get('group.members')
//Class {__each: EachProxy, isFulfilled: true, __ember1488298758311: "ember913"}

weird…


#4

Hmm, Now, I get it. There is no “direct” relationship between group and event. Either you can add hasMany in group, or remove belongsTo in event.

This should solve your issue.


#5

Yes, this resolves the issue with loading the members, but it also keeps me from implementing the actual feature.

So the event I get from the server looks like this (it contains the reference to the member):

{
  id: 1,
  member: 23
}

The members I get from the server is like this (no reference to the events):

{
  id: 23, 
  title: 'a title'
}

So the belongsTo is the only way to set up the relationship. And the hasMany is the one I actually need to display the information I want to display. But yet, I have no idea why ember data is doing what it currently is doing.


#6

This is expected behavior :slight_smile:

Member is parent entity for events. And by default, child entities are not fetched from store for obvious performance reasons.

If you require fetching child entities along with parent, you might want to use ‘include’ parameter while fetching parent entity. You can find the details here


#7

I am sorry, I don’t really understand. The event is loaded and member is the parent entity, so I would need to fetch the parent entity (the member), and not the child right?

I was under the impression that using the include parameter, would require an adapter and a server side implementation that both support that (for instance JSON api). However, I willl need to make two requests, as there is no JSON api support. One for the events and one for the members. I’ll see if I can make my adapter do something like that. On the other hand the server side could probably be changed to always embed the actual member entity, however this might lead to a lot of overhead, as one member might have had multiple updates.

Yet, I can not really understand why the default behaviour is creating an invalid record. If I just could stop ember data from doing that, I think that would solve all my issues… is there any way to do so? Right now I can’t even really figure out if the record has been correctly loaded from the server or just been automatically created by ember data.


#8

this does not occur in my example. Can you please share your code.

You can use RESTAdapter instead.

What is your backend?


#9

@Elisha.Ebenezer I am very sorry and thank you so much for the time and effort you put into this. I think I finally found my error. I still had a left over configuration in the event serializer to serialize the entities in question as embedded records. That totally makes sense now. Backend communication is over a web socket btw. But again, sorry about all the hassle. I should have realised this way earlier.


#10

Glad that you found your error. Documenting the cause may be beneficial for the rest :slight_smile: