Ember data dont want to load belongsTo relationship


#1

After 3 days fighting with ember data decided to ask community :smile: I’m trying to load data from my server to local storage according to TRANSITION.md on github

I did setup ember models:

App.Album = DS.Model.extend({
    //...
    cover: DS.belongsTo('cover', {async: true}) // tried without async as well...
});
App.Cover = DS.Model.extend({
    album: DS.belongsTo('album')
});

And maked up serializer:

        App.AlbumSerializer = DS.RESTSerializer.extend({
          serializeIntoHash: function(hash, type, record, options) {
            Ember.merge(hash, this.serialize(record, options));
          },
          extractSingle: function(store, type, payload, id) {
            var covers = payload.albums.covers;
            coverIds = covers.mapBy('id');
            payload.covers = covers;
            payload.albums.covers = coverIds;
    //console.log(JSON.stringify(payload)); -> see below
            return this._super.apply(this, arguments);
          },
        });

// console.log(JSON.stringify(payload)); -> output:

   {
       "albums":{"id":"3","uid":"3","name":"wedding","url":"http://","covers":["5"]},
        "covers":[{"id":"5","img":"img/test.jpeg","text":"test","album":"3"}]
  }

I can see loaded data in chrome extension that cover for current album uploaded correctly. BUTTT…When I try to output it: I Route

model:function(){   return this.store.find(‘album’, 3);   },

cover is always empty no matter what I try to do… I tried everything and almost give up :frowning: please help…

**My server api returns json as below(embedded relationship):

{"albums":{"id":"3","uid":"3","name":"wedding","url":"http:\/\/placehold.it\/200x100","covers":[{"id":"5","img":"img/test.jpeg","text":"test","album":"3"}]}} 

#2

hoi hoi is the question not clear or nobody can help??


#3

I’m not clear why there’s a relationship between album and cover. Cover would be a property of album, correct? As for why it’s not behaving correctly, I don’t think you can use belongsTo both directions.


#4

well ember docs say opposite: defining-models/#toc_one-to-one

App.User = DS.Model.extend({
  profile: DS.belongsTo('profile')
});

App.Profile = DS.Model.extend({
  user: DS.belongsTo('user')
});

And I dont see any reason why it shouldn’t behave correctly


#5

My mistake- I thought Ember used hasOne for 1:1 relationships.

Well, it’s not a problem with your model. I made a JS bin that proves it out with the Fixture adapter: http://jsbin.com/qaraf/1/edit. I’ve never worked with custom serializers, but the Chrome extension shows it in the store, that part should be fine. Check out my bin and see if anything jumps out at you as different.


#6

Yep, thanks for creating jsbin. Fixture adapter works just fine, i designed my project using Fixture adapter and LocalStorage adapter and CRUD working pretty well - locally. But when I tried to use backend data challenges became more complicated. Single record CRUD working good, but relationship belongsTo don’t. I have played with RESTadapter and serializer few days and still cant understand why ember wont output the “cover” relationship. I think the problem is in this json conventions…because I don’t use Rails as server backend, seems some magic happens there:). I do just like it described in the ember docs and especially TRANSITION.md … hopeless…


#7

If this is what’s coming back from your server:

{
	"albums": {
		"id":"3",
		"uid":"3",
		"name":"wedding",
		"url":"http:\/\/placehold.it\/200x100",
		"covers":[{
			"id":"5",
			"img":"img/test.jpeg",
			"text":"test",
			"album":"3"
		}]
	}
}
  1. Shouldn’t “albums” be an array?
  2. Since album/cover have a 1:1 relationship, wouldn’t the embedded property be “cover” instead of covers and be an object instead of an array?

Try getting your server to output that way and use the default serializer.

I sympathize with trying to getting the REST adapter to work with a non-Rails backend. It can get a little rough- stick with it!

EDIT: I would expect to see this for a request to /albums/3:

{
	"album": {
		"id":"3",
		"uid":"3",
		"name":"wedding",
		"url":"http:\/\/placehold.it\/200x100",
		"cover": {
			"id":"5",
			"img":"img/test.jpeg",
			"text":"test",
			"album":"3"
		}
	}
}

And this for a request to /albums:

{
	"albums": [{
		"id":"3",
		"uid":"3",
		"name":"wedding",
		"url":"http:\/\/placehold.it\/200x100",
		"cover": {
			"id":"5",
			"img":"img/test.jpeg",
			"text":"test",
			"album":"3"
		}
	}]
}

#8

My friend “albums” should be array in case you do findAll e.g. this.store.find(‘album’), but now I am working with single record this.store.find(‘album’, 3) so it should be a single object.

  1. Well good point, I had same thoughts. But when I tried to send from server such json

    “cover”:[{ “id”:“5”, “img”:“img/test.jpeg”, “text”:“test”, “album”:“3” }] I got “typeKey” error what means that ember REST adapter prefer plural form of model names in json object. Probably it could be tuned somewhere in adapter but this is not actually point, because emberjs expect from the server sideloaded relationships what I actually tried to do in Serializer.

You advice me to use default serializer. But I actually do use it.

I would expect to see this for a request to /albums/3:

That json which you provided as example will not work since as I mentioned above - REST adapter expect plural form of model names no metter what request you do - find, or findAll (e.g./albums/3 or /albums/ ). Thats lil bit strange but that how ember data works:).

Kyle thanks for your answers and I appreciate your will to help, but seems we are both have limited understanding how ember data works and need advice of some emberjs Experts :(, the question is where to find them :).


#9

Ember Data doesn’t pluralize object names no matter what- only REST endpoints URLs (which is the standard). From the guide:

Ember Data expects that a GET request to /posts/1 would return the JSON in the following format:

{
  "post": {
    "id": 1,
    "title": "Rails is omakase",
    "comments": ["1", "2"],
    "user" : "dhh"
  },

  "comments": [{
    "id": "1",
    "body": "Rails is unagi"
  }, {
    "id": "2",
    "body": "Omakase O_o"
  }]
}

If it will always be singular, use the singular form and use an object. If it could be plural, use the the plural form and use an array.

In your first post, you said you were using a custom serializer- I assumed you were still using it. Have you tried not embedding the relationship at all, just pointing to the ID? If async is on, it will GET the objects as separate requests. It would be useful for troubleshooting if nothing else.

I’m sorry I haven’t been more successful in solving your problem, but I do have 3 or 4 of these of these Ember Data REST applications working in the wild, and this is how I built them. If anyone else would like to weigh in on this, have at.


#10

Well I afraid or I do something wrong or ember guide is not perfect, because when I return model name in single form from server, ember get me such error:

Error while loading route: TypeError: Cannot read property 'typeKey' of undefined
    at Ember.Object.extend.modelFor (http://builds.emberjs.com/beta/ember-data.min.js:10:5715)
    at Ember.Object.extend.recordForId (http://builds.emberjs.com/beta/ember-data.min.js:10:2297)
    at g (http://builds.emberjs.com/beta/ember-data.min.js:9:29324)
    at http://builds.emberjs.com/beta/ember-data.min.js:9:29061
    at http://builds.emberjs.com/beta/ember-data.min.js:9:27314
    at Object.OrderedSet.forEach (http://server/ember-tests/js/libs/ember-1.5.0.js:3271:10)
    at Object.Map.forEach (http://server/ember-tests/js/libs/ember-1.5.0.js:3426:10)
    at Function.f.reopenClass.eachRelationship (http://builds.emberjs.com/beta/ember-data.min.js:9:27290)
    at f (http://builds.emberjs.com/beta/ember-data.min.js:9:28901) 

So I set up server API to return model names in plural form and so far it’s working. Have no idea what does this error mean.

Well I’m extending default DS.RESTSerializer as its recommended in TRANSITION doc, is that means that I use the custom one? I think its same.

Have you tried not embedding the relationship at all, just pointing to the ID? If async is on, it will GET the objects as separate requests. It would be useful for troubleshooting if nothing else.

For now from my server API because of different reasons I could receive json with only embedded relationships:(

I didn’t want to offend you, sorry if I did. Regarding your apps - thats great. Did you used ember data for your apps?


#11

@alekso Did you find a fix for this issue? Having a similar issue getting the same error Cannot read property 'typeKey' of undefined.


#12

Bumping this. I am having the same problem as well when dealing with embedded records. My setup is as follows, hopefully this helps some:

app/routes/index.js

import Ember from 'ember';

export default Ember.Route.extend({
    model: function() {
        return this.store.find('dashboardProfile');
    }
});

app/serializers/application.js

import DS from 'ember-data';

export default DS.RESTSerializer.extend({
    keyForAttribute: function(attr) {
        return attr.capitalize();
    },
    keyForRelationship: function(attr) {
        return attr.capitalize();
    }
});

app/models/dashboard-profile.js

import DS from 'ember-data';

export default DS.Model.extend({
    userUid: DS.attr('string'),
    clientUserUid: DS.attr('string'),
    profile: DS.belongsTo('profile')
});

app/serializers/dashboard-profile.js

import DS from 'ember-data';
import AS from 'dashboard/serializers/application';

export default AS.extend({
    primaryKey: 'UserDashboardProfileId',
    attrs: {
        profile: { embedded: 'always' }
    }
});

app/models/profile.js

import DS from 'ember-data';

export default DS.Model.extend({
    name: DS.attr('string'),
    privilegeId: DS.attr('number'),
    dashboardProfile: DS.belongsTo('dashboardProfile')
});

app/serializers/profile.js

import DS from 'ember-data';
import AS from 'dashboard/serializers/application';

export default AS.extend({
    primaryKey: 'ProfileId'
});

The JSON that is returned from the server is as follows:

{
    "DashboardProfiles": [
        {
            "UserDashboardProfileId": "67a540ca-1184-486c-984e-3163e7cbf31a",
            "UserUid": "ad53ff7c-f063-e211-aba0-001560a8fe79",
            "ClientUserUid": "ae53ff7c-f063-e211-aba0-001560a8fe79",
            "Profile": {
                "ProfileId": 1,
                "Name": "My Profile",
                "PrivilegeId": 241,
                "ProfileWidgets": null
            }
        },
        {
            "UserDashboardProfileId": "7fd13935-3a26-4e10-a661-8efbc5359f84",
            "UserUid": "ad53ff7c-f063-e211-aba0-001560a8fe79",
            "ClientUserUid": "ae53ff7c-f063-e211-aba0-001560a8fe79",
            "Profile": {
                "ProfileId": 2,
                "Name": "My Team",
                "PrivilegeId": 241,
                "ProfileWidgets": null
            }
        }
    ]
}

#13

To follow up on this, the reason I was receiving the error is because I did not include the DS.EmbeddedRecordsMixin in my dashboard-profile.js serializer. After adding that, I started receiving an error that boiled down to there being problems with the updatePayloadWithEmbeddedBelongsTo method in the EmbeddedRecordsMixin. It looks like these issues are resolved in the canary build of ember-data. I have an issue open on Github that shows how I updated the updatePayloadWithEmbeddedBelongsTo method if anyone is interested though:

The canary build of ember-data has overhauled much of the EmbeddedRecordsMixin, so once that gets pushed into the beta I think we’ll see much better support for this kind of thing.