EmberJS / Ember-data: hasMany collection incomplete, despite all IDs exist
Asked Answered
E

1

7

installed versions

ember-cli   2.14.2
ember-data  2.14.10

A little perspective:

I have a service called menu that performs store queries inside computed properties. One of these store queries is behaving rather weird. It fetches all records under the model name product-segment from a fully functional JSON API. This model has a n-n relationship to a model called product, referenced through hasMany DS objects:

models/product-segment.js

export default DS.Model.extend({

    products: DS.hasMany('product'),
    // ...
});

models/product.js

export default DS.Model.extend({

    productSegments: DS.hasMany('product-segment'),
    // ...
})

The problem:

Now, when I fetch these product-segment models, I instruct the API to { include: 'products' }, and the API does as is requested. The response includes 15 related product models for a particular product-segment, which is correct.

(let's call this particular product-segment segment x, it's the subject for all my debugging info below)

However, accessing the relationship collection on segment x from any context, at any time, only returns me 12 models, so 3 are missing. I witnessed similar issues with other product-segment models, so I don't think it's an issue with one specific model.

More perspective

I initially thought I was dealing with a race condition of some kind, and to be sure I created a computed property - test - to find out, and I dumped {{menu.test}} into my view to tickle the computed prop.

Here's the bare minimum info inside services/menu.js

export default Service.extend({

    store: inject(),

    activeProductSegment: null,

    // As a note: this does not trigger an infinite loop
    productSegments: computed('store.product.[]', 'store.product-segment.[]', function() {
        return get(this, 'store').findAll('product-segment', { include: 'products' });
    }),

    test: computed('activeProductSegment', function() {
        let segment = get(this, 'activeProductSegment');

        if (segment) {
            console.log(segment.hasMany('products').ids());
            console.log(get(segment, 'products').mapBy('id'));
        }
    }),
});

The property activeProductSegment is being set to different product-segment model instances through an action of a component , which looks like this:

export default Component.extend({
    menu: inject(), // menu service is injected here...

    actions: {
        setProductSegment(segment) {
            get(this, 'menu').set('activeProductSegment', segment);
        }
    }
});

The action itself works as expected, and is actually quite unrelated to my problem. activeProductSegment is never updated in any other way. The view passes this action product-segment model objects:

{{#each menu.productSegments as |segment|}}
    <li {{action 'setProductSegment' segment}}>{{segment.name}}</li>
{{/each}}

Trouble starts here

I set menu.activeProductSegment to segment x by clicking its associated <li> element.

When I now try to get all related product models of segment x, only 12 of 15 models are present within the returned collection. To be sure that the JSON response was really fine (i.e. the type definitions etc. are right) I checked the amount of product IDs that were registered at segment x. I logged the following line (the context of the logs below is in the Ember.Service snippet above):

console.log(segment.hasMany('products').ids());

That returned me an array with 15 correct IDs, so segment x has all id's as it is supposed to have. All product models of those id's have been included in the response, so I suppose there should be no issue with async data of some kind. Still, the following line gave me back an array of 12 id's:

console.log(get(segment, 'products').mapBy('id'));

I tried putting the 2 logs into a 2-second setTimeout, but the result stayed identical:

This is the 2 logs

I'm starting to think this is a bug, since I noticed that the first time that an id was not accompanied by a model, is when for the first time the next ID is lower than the preceding ID.

Update on the above I tried a different order in the response, and note the second and the third id's: "7", "6". Guess this is not the problem:

enter image description here

Unless I misunderstand, the models should be live, so any relationship is supposed to update as data becomes available. I think it is very unlikely this has anything to do with malformed data.

What could be the cause for the missing models in the hasMany relationship collection, despite the fact that all necessary ids are properly registered at the hasMany relationship object, and we're not required to await arrival of any async/network data at this point? And what might be a suitable solution to the problem?

Essie answered 24/8, 2017 at 19:25 Comment(6)
The CP productSegments gets recomputed just once? This might be tricky since it depends on 'store.product.[]', 'store.product-segment.[]' which actually change as the CP's function (this.store.findAll()) pushes records into the store, doesn't it? This is pure guess but worth further examinationAlternately
I also remember I had similar problem but it rooted in using nested records in another record. Something Ember-data recommends not to do. Since my content model might but not necessarily had to load related models localized-content, I ended up checking its state as follows: // app/models/content.js isFullyLoaded: computed('localizedContent', function () { return Ember.isPresent(this.hasMany('localizedContent').value()); }),Alternately
@Alternately I had that exact concern too, but this does not trigger an infinite loop. If the store gets a query command identical to one previously given, it will, instead of again contact the API, just give back cached content, unless explicitly told otherwise.Essie
Ok, one attempt more. What do you see when running this: get(segment, 'products').then(()=>console.log(get(segment, 'products').mapBy('id'))); Could it be that you truly run in a race condition and explicitly requesting relationship resolution solves the problem?Alternately
Ok I tried, exact same thing. It's also funny that its alway 12 id's, never 13, never 11. I'm just clueless :PEssie
I remember I had completely same symptoms but can't remember how I solved it. Bugs me no less than it bugs you now :DAlternately
S
0

I know it does not appear to be related to async issues, but I would still try defining the hasMany as not async:

products: DS.hasMany('product', {async: true}),
Soukup answered 28/12, 2017 at 10:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.