This framework drives me crazy
I habe a model lets name it parent which has a “hasMany” relation to a modelnamed child. Everything works fine and the data is loaded properly.
I have another hasMany relation on child toa model named child2 and the data is not loaded although everything is exactly configured as in the first hasMany relation (except the model names).
I have no idea on how to resolve this issue
Hi Marco! Ember Data can definitely be a lot to grapple with. FWIW Ember Data is currently in the middle of a big overhaul so a lot of this stuff should be more explicit and more flexible in the future.
But on to your actual problem… Are these both async relationships (the default)? I assume yes, so you’re expecting implicitly loaded async hasMany relationships.
So I’ll imagine you have code like this:
// app/models/parent.js
...
@hasMany('child') children;
...
// app/models/child.js
...
@hasMany('grandchild') grandchildren;
...
// app/models/grandchild.js
...
And presumably you’re trying to fetch all the data like:
{{#each someParent.children as |child|}}
{{#each child.grandchildren as |grandchild|}}
<li>{{grandchild.name}}</li>
{{/each}}
{{/each}}
But who knows. Let me know if you’re doing something different e.g. in JS.
What happens next also depends on your backend and the data that it provides. It could be using JSON:API and providing either ids or links for relationships. Or it could be a different format and be providing ids:
children_ids: ['some-child-id', 'another-child-id', ...],
Anyway at this point what we’d expect to happen is the first each
references parent.children, which triggers one ore more async requests like:
GET /children?parent=parentid
(if using relationship links with JSON:API)- or a bunch of requests like:
GET /children/some-child-id
(if using ids without coalescing) - or one or more requests like
GET /children?ids[]=some-child-id&ids[]=another-child-id
(if using ids with coalescing)
Next it would render the “inner” grandchild loops, which evaluates child.grandchildren
for each child, and that would start another series of async requests exactly like above except to /grandchildren?...
or /grandchildren/some-id
.
Anyway to debug properly this we’ll need some more information:
- are the relationships in question async (I assume yes)?
- how are you trying to use/fetch them? Similar to the template snippet above? Or in javascript?
- does your backend return links or ids for relationships? (sample payloads coule be helpful)
- what adapter/serializer are you using?
- what ember data version do you have? (not super relevant but possibly helpful)
- is your app making the network request for the grandchildren? If yes it may be a serialization/adapter issue, if no then it might be your relationship linkage
- does it work if you have a single child record and you attempt to resolve the grandchildren?
If you really want to dive into the debugging hole you can use Ember Inspector to look at your store and find a child record and send it to the console with the >$E
button and then in the console use the hasMany() API to see what’s going on:
// if you use ids, this should return whatever the child think it's grandchildren ids are
$E.hasMany('grandchildren').ids();
// if you use links, this should return the link it thinks it should use
$E.hasMany('grandchildren').link();
// try triggering the fetch manually
$E.hasMany('grandchildren').load();
$E.hasMany('grandchildren').reload();
Anyway hope some of the above is useful but if not providing answers to some of those questions could help us dig in a little more.
Thanks for your answer: Here some more informations regarding my issue:
This is the top level parent class, which has a hasMny relation to “einsatz” which is reolved properly
export default class TeilnehmerModel extends Model {
@hasMany('einsatz', { async: true, inverse: null }) einsatzs;
}
The child has another hasMany relationship, which is not resolved
export default class EinsatzModel extends Model {
@hasMany('einsatzAbteilung', { async: false, inverse: null }) einsatzAbteilungs;
}
and this the definition of the secnd child:
export default class EinsatzAbteilungModel extends Model {
}
This is the parent payload:
This the child payload
@hasMany('einsatzAbteilung', { async: false, inverse: null }) einsatzAbteilungs;
^ this won't automatically fetch
I think the problem is that your child has defined a sync relationship to the grandchild model. If you were using ids this would probably error but you’re using links so it just doesn’t fetch (it expects the data to already be there, which is basically what a sync relationship means).
In the docs it says:
If you are using
links
with sync relationships, you have to use the HasMany reference API to fetch or refresh related resources that aren’t loaded.
So you have a few options:
- if you want it to behave like the other relationship you can simply change it to
async: true
- if your backend supports the
include
feature you could sideload the grandchild records with the child records - you could manually fetch the sync relationship as the docs say:
einsatz.hasMany('einsatzAbteilung').reload()
but you’ll have to do that for eacheinsatz
loaded from theteilnehmer
I finally I made it but …
For each model in my ember app I need to implement a serializer although I am using a JsonRestApiAdapter (for my understanding it should work out of the box) and in my seralizer “EinsatzAbteilung” I need to overwrite the method
import JSONAPISerializer from '@ember-data/serializer/json-api';
export default class EinsatzAbteilungSerializer extends JSONAPISerializer {
modelNameFromPayloadKey(key) {
switch(key) {
case 'abteilungs' : return 'abteilung';
case 'einsatzAbteilungs' : return 'einsatzAbteilung';
default : return this._super(key);
}
}
}
Otherwise I get an error schema forkey not found (with no further information which key :-().
Thanks @dknutsen for your help !
Ah yes, some backends (e.g. Rails) prefer pluralized model names while Ember Data uses singularized. We do similar things in the app I work on. One thing that might make this a little more palatable is making it generic by using an application serializer if you’re able (if not the alternative would be a base serializer and simply re-exporing the base serializer for each model you need.
Then instead of manually adding the relationships like you’re doing you can use the singularize
method from ember-inflector.
import JSONAPISerializer from '@ember-data/serializer/json-api';
import { singularize } from 'ember-inflector';
export default class EinsatzAbteilungSerializer extends JSONAPISerializer {
modelNameFromPayloadKey(key) {
return singularize(key);
}
}