ember-data-sails-adapter icon indicating copy to clipboard operation
ember-data-sails-adapter copied to clipboard

Error with nested association on SailsRESTAdapter (either a bug or unclear documentation)

Open leejt489 opened this issue 11 years ago • 5 comments

Hi, I am having an issue with loading nested associations from Sails, which is what I thought this project addressed. I am under the impression that two of the main incompatibilities between Sails and Ember (apart from pluralize and CAML casing) are that the Ember RESTSerializer wants a root element in the JSON response, and that it wants associations to be sideloaded. Using the JSONSerializer seems to take care of the former, but using the SailsRESTAdapter I am still having problems with sideloading. I am using ember-data 1.0.0.beta-10

The data from the server for the SurveyTemplateDisplay model that gets passed to the serializer looks like this:

{
"language": {
"name": "english",
"id":"xxxxxxxxxxxx"
},
"displayName": "My display name for english",
"description": "Test description",
"id": "yyyyyyyyyyyyy"
}

and my ember app looks like this:

window.App = Ember.Application.create();

App.ApplicationAdapter = DS.SailsRESTAdapter.extend({
    pathForType: function(type) {
        var camelized = Ember.String.camelize(type);
        return Ember.String.pluralize(camelized);
    },
    namespace: 'api/v1'
});

App.Router.reopen({
    rootUrl: '/app'
});

App.Router.map(function() {
    this.resource('surveyTemplateDisplay', {path: '/surveyDisplay/:id'});
})

App.SurveyTemplateDisplayRoute = Ember.Route.extend({
    model: function(params) {
        return this.store.find('surveyTemplateDisplay', params.id);
    }
});

App.SurveyTemplateDisplay = DS.Model.extend({
    displayName: DS.attr('string'),
    description: DS.attr('string'),
    language: DS.belongsTo('language')
});

App.Language = DS.Model.extend({
    name: DS.attr('string')
});

The basic problem is that when the JSONSerializer attempts to deserialize the associated model (http://builds.emberjs.com/beta/ember-data.js line 11276), it is expecting there to be a property 'type' on the nested data (the "language": {....} object that is not there. Changing that line from data[key] = store.recordForId(id.type, id.id); to data[key] = store.recordForId(association.key, id.id); seems to do the trick.

Is this an ember-data bug? Can this behavior be overwritten by extending the SailsRESTAdapter or the JSONSerializer instead of hacking ember-data?

Thank you

leejt489 avatar Sep 17 '14 22:09 leejt489

@leejt489 check related data: https://github.com/mphasize/sails-ember-blueprints and https://github.com/mphasize/sails-ember-blueprints/issues/4#issuecomment-54087718

albertosouza avatar Sep 18 '14 11:09 albertosouza

@albertosouza yeah I started there, but didn't really want to change the api, so I tried to implement this ember-data-sails-adapter, but then got stuck on this and decided it would probably be better performance to change the api anyways, so I am going with the sails-ember-blueprints now (which is a good deal cleaner now than when I first looked at it a couple months ago).

leejt489 avatar Sep 18 '14 17:09 leejt489

Yeah it appears this project it out of date with the latest sails and ember data and I've been super swamped with work so I haven't had the time to give it the attention is deserves.

To make relationships work correctly with ember data 1.0.0.beta-10 you will need to create a custom serializer for SurveyTemplateDisplay that includes the JSONSerializer with the embedded record mixin. For example:

App.SurveyTemplateDisplaySerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
  attrs: {
    language: {embedded: 'always'},
  }
});

Another issue that was reported with ember data 1.0.0.beta-10 is that it sends its put and post payload inside a data property which confuses sails. The long term solution is to overwrite the adapter's createRecord and updateRecord methods to not nest the payload in the data key. https://github.com/bmac/sails-ember-cli-test/blob/master/ember-client/vendor/ember-data-sails-adapter.js#L53-L72

bmac avatar Sep 18 '14 18:09 bmac

@bmac thanks for the update and your work on this. I spent some time working with the Serializer; but, unless I am missing something, it doesn't get you all the way there. Because sails by default does not recursively populate (nest) associations, the {embedded: 'always'} flags causes problems.

For example, suppose I have a Business that has an owner property which references a User model. Both Business and User have references to an Address object.

By default, GET business:/id will return something like

{
    "owner": {
        "id": "xxxxxxxxxx",
        "username": "someusername",
        "address": "yyyyyyyyyy"
    },
    "address": {
        "city": "TheCity",
        "anotherAddressField": "value",
        "id": "zzzzzzzzzz"
    },
    "id": "aaaaaaaaaa"
}

If use the EmbeddedRecordsMixin with {embedded: 'always'} for the AddressSerializer, it will cause an error because sometimes the JSON is embedded and sometimes it is just an id (when the serializer tries to extract the owner it will expect its address to be an embedded object).

It looks like to me you would have to extend the RESTSerializer and get into extending the normalize, extract, and serialize methods to make it work, or create a custom Mixin. Since I control the api, and sideloading will be better for ember performance and minimizing response data, I decided to go that route.

But I still think this is an awesome project for instances where one doesn't have control of the API! I would like to see both Sails and Ember have some sort of config options to specify JSON formats instead of the current options of overwriting blueprints or serializers.

leejt489 avatar Sep 18 '14 21:09 leejt489

Thanks for the info. In my experience controlling the api to work with the RESTSerializer will save you a ton of headaches in the long term.

bmac avatar Sep 18 '14 22:09 bmac