Trying to make findAll return multiple type of models

In a route’s model hook, I send a request to the server for all Journal models:

this.store.findAll('journal');

Thing is, Journal is actually just an abstract class for anything “journal”. For example, I have a Report and a Measurement model both extending from Journal. The server reponse (using REST) returns something like this:

{
  "Journals": [
    { "id": "report_1", "type":"Report", ... },
    { "id": "measurement_1", "type":"Measurement", ... }
  ]
}

What I try to accomplish: a) Make sure they are normalized to the right models; b) Make sure findAll returns an array containing all these different types of records.

The first step is no problem. There are several hooks in either an adapter (handleResponse) or a serializer (normalize, normalizeFindAllResponse) to achieve this. Using the Ember Inspector I can see I have a Report and Measurement record pushed to the store instead of two Journal records.

The second step is currently giving me a headache though. Whatever I try, the findAll always returns an empty array. Whatever hook I use to normalize to the right model, these models are somehow excluded from the findAll reponse by the “magic” of Ember Data.

So I created a custom findAll method on the journal adapter which looks something like this:

findAll() {
  const url = this.buildURL('journal', null, null, 'findAll');
  return this.get('ajax').request(url).then(payload => {
    return payload.map(journal => {
      return this.get('store').push({
        data: {
          id: journal.id,
          type: journal.type,
          attributes: journal
        }
      });
    });
  });
}

Once again, the right records are pushed to the store, but once again, the route’s model hook receives an empty array. And once again, I’m wondering what Ember Data is doing “behind the scenes” to prevent me from returning an array containing all records. Additionally this also creates the next warnings:

WARNING: Encountered "0" in payload, but no model was found for model name "0" (resolved model name using care-home@serializer:journal:.modelNameFromPayloadKey("0"))
WARNING: Encountered "1" in payload, but no model was found for model name "1" (resolved model name using care-home@serializer:journal:.modelNameFromPayloadKey("1"))

Ofcourse I can ignore adapters/serializers and create a service with a method doing exactly what I want. But I’m trying to stick as close as possible to Ember Data since it’s a part of this project and this should be something I can accomplish using adapters/serializers.

Any help is greatly appreciated. Thanks. :slight_smile:

1 Like

Perhaps you can create a hash like this ember-cli-blog/posts.js at master · broerse/ember-cli-blog · GitHub and use setupController to set the models in this hash…

Depending on your server configuration, Ember Data can handle polymorphic models just fine. For example, when using JSONAPI, I know that it works quite well.

Basically, you’d just make models that extend from the same base model, e.g.:

// app/models/journal.js
export default DS.Model.extend({
  // shared attributes go here
});
// app/models/report.js and app/models/measurement.js
import Journal from './journal';
export default Journal.extend({
});

You should then be able to do store.findAll('journal') as well as store.findAll('measurement'), which will fetch from the API at /journals or /measurements respectively (depending on your adapter). This requires that the API response contains the model type, which seems to be the case in your example. So when fetching from /journals it can contain both measurements as well as reports.

A model can then even have a mixed relationship, e.g. journals: DS.hasMany('journal')

1 Like

@francesconovy Actually I like you answer but I have issues proving that its right. I tried a simple setup with mirage and the following models:

// app/models/animal.js    
import DS from 'ember-data';
export default DS.Model.extend({
    legs: DS.attr('number')
});

// app/models/dog.js
import DS from 'ember-data';
import Animal from './animal';

export default Animal.extend({
    barks: DS.attr('boolean')
});

// app/models/snake.js
import DS from 'ember-data';
import Animal from './animal';

export default Animal.extend({
    slithers: DS.attr('boolean')
});

I set up a route that should fetch in the model hook via

this.get(‘store’).findAll(‘animal’);

all animals. This sends a GET request to /api/animals. The API is mimicked with mirage and returns the following JSONAPI conform data:

{
    "data": [
        {
            "id": 1,
            "type": "dog",
            "attributes": {
                "legs": 4,
                "barks": true,
            }
        },
        {
            "id": 2,
            "type": "snake",
            "attributes": {
                "legs": 0,
                "slithers": true,
            }
        },
    ]
}

Unfortunately this still leads to the behavior of returning an empty array in the model as was already observed by @Bauke.

@francesconovy Do you have a hint how this is done correctly?

Kind regards,

Sebastian

It has been quite a while that I’ve used this, so I am not 100% sure. But I think it did work for me. You’d probably have to hook into the adapters/serializer to see where it goes wrong.

Could you solve this problem? The way to solve it that I found was to change the findall by query, but I don’t know if it has any negative implication.

In your case it would be:

this.get('store').query('animal', { });