Ember-Data Embedded hasMany Record Issue


#1

Hi there,

I am having a very hard time working with some ember data models. I am working with ember app kit as well as ember-data-shim 1.0.0-beta.3. Here are my models:

    var Route = DS.Model.extend({
    name: DS.attr(),
    description: DS.attr(),
    creation: DS.attr(),
    modified: DS.attr(),
    active: DS.attr(),
    adhoc: DS.attr(),
    routeUsers: DS.hasMany('user'),
    userapprovals: DS.hasMany('userapproval')
});

export default Route;

var UserApproval = DS.Model.extend({
    route: DS.belongsTo('route'),
    user: DS.belongsTo('user'),
    amount: DS.attr(),
    approval: DS.attr(),
    comment: DS.attr(),
    notification: DS.hasMany('notification', {embedded: 'always'}),
    denyRedirect: DS.belongsTo('user')
});

export default UserApproval;

var User = DS.Model.extend({
    firstname: DS.attr('string'),
    lastname: DS.attr('string'),
    username: DS.attr('string'),
    email: DS.attr('string'),
    password: DS.attr('string'),
    
    fullName: function(){
        return this.get('firstname') + ' ' + this.get('lastname');
    }.property('firstname', 'lastname')
});

export default User;

Here are my serializers:

var ApplicationSerializer = DS.ActiveModelSerializer.extend({
    normalize: function (type, hash, property) {
        // normalize the `_id`
        var json = {
            id: hash._id
        };
        delete hash._id;

        // normalize the underscored properties
        for (var prop in hash) {
            json[prop.camelize()] = hash[prop];
        }

        // delegate to any type-specific normalizations
        return this._super(type, json, property);
    },
});

export default ApplicationSerializer;

import ApplicationSerializer from 'appkit/serializers/application';

var RouteSerializer = ApplicationSerializer.extend({
    attrs: {
        routeUsers: {
            embedded: 'always'
        },
        userapprovals: {
            embedded: 'always',
            ids: true
        }
    }
});

export default RouteSerializer;

    import ApplicationSerializer from 'appkit/serializers/application';

var UserApprovalSerializer = ApplicationSerializer.extend({
    attrs:{
        notification: {
            embedded: 'always'
        }
    }
});

export default UserApprovalSerializer;

I can save models just fine to mongodb, but when the model is sent back my embedded records disappear. Here is my JSON response:

{
  "route": {
    "name": "4",
    "description": "123",
    "adhoc": true,
    "_id": "527822dee56a06dc2a000002",
    "__v": 0,
    "userapprovals": [
      {
        "amount": "22",
        "route_id": null,
        "user_id": "5277ca37b900b14c0e000003",
        "deny_redirect_id": "5277ca25b900b14c0e000002",
        "id": "f0ced142-aee1-4abb-f36a-56ae0088141b"
      }
    ],
    "route_users": [
      {
        "firstname": "first",
        "lastname": "last",
        "username": "flast",
        "email": "flast@xyz.com",
        "password": null,
        "id": "5277ca37b900b14c0e000003"
      }
    ]
  }
}

I guess what I am trying to figure out is what is wrong with my JSON response to cause the embedding relationships to disappear? I have also tried overriding the extractSingle in my RoutesSerializer with the following:

extractSingle: function(store, type, payload, id, requestType) {
        var userApprovals = payload.route.userapprovals,
            approvalIds = userApprovals.mapProperty('id');
    
        payload.userapprovals = userApprovals;
        payload.route.userapprovals = approvalIds;
    
        return this._super.apply(this, arguments);
    }

But when I override that method I get a error stating You must include an id in a hash passed to push. When I remove the extractSingle override I get no error’s but my model does not contain the embedded records. I’ve been fiddling with it all day and I think it might be time to get another set of eyes to look at it. Please let me know if I need to provide anything else. Thanks for the help in advance.


#2

You need to provider the serializer with a different primary key. It is asking for “id”, but yours uses an underscore before the id. Add a “primaryKey” property with the pattern you are looking for and it should work.


#3

I apologize I forgot to point out that my parent model route comes back without issues and gets rendered into the template. It is the hasMany relationships that do not get placed into the store correctly. I can view the parent model in the ember chrome extension and data is populating into my template such as name and description. When I look at the model using the ember chrome extension I see that the parent model has userapprovalIds and routeUserIds so it’s trying to sideload the data. Both of those attributes have the correct Id’s in them, it’s just that no data is being sideloaded from the embedded models.

**** Update ****

So searching through using the ember chrome extension I can see that the store has my embedded records loaded in it’s typeMap. Why would they be loaded in the typeMap and linked, but not be showing appropriately on my template? In my template I am using the #each helper on the userapprovals and the routeUsers relations, but I am not seeing any information in the template show from the embedded records. It’s like the hasMany hasn’t fully loaded or something. Here is a copy paste from the chrome console on the route model:

adhoc: true
description: "something here"
id: "527948268a65b92022000001"
name: "14"
routeUserIds: Array[1]
 0: "5277ca25b900b14c0e000002"
 length: 1
 __proto__: Array[0]
routeUsers: undefined
userapprovalIds: Array[1]
 0: "017b4fe7-9f98-4bce-9c8d-089309b7729a"
 length: 1
 __proto__: Array[0]
userapprovals: undefined
v: 0
__proto__: Object

#4

@jarremw the embedded fix for Active Model Serializer adapter was committed on 10/3/2013 but the 1.0.0-beta3 was released on 9/28/2013 … just update your ember data to the latest using Canary branch… http://builds.emberjs.com/canary/ember-data.js it works for me!

// Version: v1.0.0-beta.3 // Last commit: 2259c27 (2013-09-28 19:24:07 -0700)

And the PR for the fix is here:


#5

It still is not working for me. When I look at the model with the ember extension I still see that my userApprovals is undefined but the userApprovalIds is filled with the ids. A couple of questions.

  1. I do have Id’s on my embedded data, could this be causing the problem?
  2. I have my userApprovals marked as hasMany(‘userApproval’), do I need to remove that?
  3. Do I need to do something in my setupController to fetch the other models for my hasMany relationships?

What I am not understanding is why the userApproval records would be populated in the store, I can see them when I call this.get(‘store’).all(‘userApproval’), and not show up for my route model. They all included in the some payload as the route, embedded in the route. It looks like they are being extracted just fine since I have userApprovalIds in my route model. I guess what am I missing in my setup that could be causing my hasMany to be undefined from the parent model? Once again I am using Ember App Kit, mongodb, the latest canary of both ember and ember data.

Thanks for the help so far!


#6

I was able to resolve my issue by moving away from ember-data and implementing ember-model. It seemed to work better out of the box for my situation since I am working with mongo and a lot of embedded records.

If you would like to know more about my setup and why I chose to go with ember-model please message me.

Thanks for the help, Jarred


#7

Did you try to use async relationships? Maybe this is what is going on. Try hasMany(‘userApproval’, {async: true}) and see if that works out.