1

I'm experimenting with Sails.JS and Ember.JS.

Unfortunately in this case Ember expect the json to be in http://jsonapi.org format. Sails.js JSON looks like this:

// specific game
// /api/game/1
{
    cards: [
        {
            id: 2,
            createdAt: "2014-04-10T13:15:47.259Z",
            updatedAt: "2014-04-10T13:15:47.259Z",
            game: 1,
            player: 1
        }
    ],
    table: {
        id: 1,
        createdAt: "2014-04-10T10:47:27.292Z",
        updatedAt: "2014-04-10T10:47:27.292Z"
    },
    serverSeed: "test",
    createdAt: "2014-04-10T13:15:03.872Z",
    updatedAt: "2014-04-10T13:15:03.872Z",
    id: 1
}

// all games
// api/game
[
    {
        cards: [
            {
                id: 2,
                createdAt: "2014-04-10T13:15:47.259Z",
                updatedAt: "2014-04-10T13:15:47.259Z",
                game: 1,
                player: 1
            }
        ],
        table: {
            id: 1,
            createdAt: "2014-04-10T10:47:27.292Z",
            updatedAt: "2014-04-10T10:47:27.292Z"
        },
        serverSeed: "test",
        createdAt: "2014-04-10T13:15:03.872Z",
        updatedAt: "2014-04-10T13:15:03.872Z",
        id: 1
    }
]

Are there any good ways to either convert the Sails.js JSON or a Ember.js Serializer that fixes this problem?

Martijn
  • 373
  • 1
  • 4
  • 15
  • which format is which? Either way, the only difference is one response is in an array, the other isn't. Either put the object response into an array, or access the array response at the 0 index – thescientist Apr 10 '14 at 13:52
  • Both are Sails.JS responses. The first one is a call to `/api/game/1`, so only ID 1, the second is a call to `/api/game` so all games (hence the array) are shown, if I had more games the array would be bigger of course. – Martijn Apr 10 '14 at 13:54
  • I just wanted to point out that Ember-Data as of beta 7 does not exactly conform to the spec at http://jsonapi.org which is very confusing. For example, jsonapi requires all responses be wrapped in an array even if you return only one item (ie.. /games/1) but ember-data only wants results wrapped in an array if you're returning multiples (i.e., /games). I previously posted with an example of what ember data wants its json to look like here http://stackoverflow.com/a/21967780/2853094 – Sarus Apr 11 '14 at 00:15

1 Answers1

0

I changed the code on http://mozmonkey.com/2013/12/loading-json-with-embedded-records-into-ember-data-1-0-0-beta/ a bit and now got it working.

App.ApplicationSerializer = DS.RESTSerializer.extend({
    /**
     The current ID index of generated IDs
     @property
     @private
     */
    _generatedIds: 0,

    /**
     Sideload a JSON object to the payload

     @method sideloadItem
     @param {Object} payload   JSON object representing the payload
     @param {subclass of DS.Model} type   The DS.Model class of the item to be sideloaded
     @param {Object} item JSON object   representing the record to sideload to the payload
     */
    sideloadItem: function(payload, type, item){
        var sideloadKey = type.typeKey.pluralize(),     // The key for the sideload array
            sideloadArr = payload[sideloadKey] || [],   // The sideload array for this item
            primaryKey = Ember.get(this, 'primaryKey'), // the key to this record's ID
            id = item[primaryKey];

        // Missing an ID, generate one
        if (typeof id == 'undefined') {
            id = 'generated-'+ (++this._generatedIds);
            item[primaryKey] = id;
        }

        // Don't add if already side loaded
        if (sideloadArr.findBy("id", id) != undefined){
            return payload;
        }

        // Add to sideloaded array
        sideloadArr.push(item);
        payload[sideloadKey] = sideloadArr;
        return payload;
    },

    /**
     Extract relationships from the payload and sideload them. This function recursively
     walks down the JSON tree

     @method sideloadItem
     @param {Object} payload   JSON object representing the payload
     @paraam {Object} recordJSON   JSON object representing the current record in the payload to look for relationships
     @param {Object} recordType   The DS.Model class of the record object
     */
    extractRelationships: function(payload, recordJSON, recordType){

        // Loop through each relationship in this record type
        recordType.eachRelationship(function(key, relationship) {
            var related = recordJSON[key], // The record at this relationship
                type = relationship.type;  // belongsTo or hasMany

            if (related){

                // One-to-one
                if (relationship.kind == "belongsTo") {
                    // Sideload the object to the payload
                    this.sideloadItem(payload, type, related);

                    // Replace object with ID
                    recordJSON[key] = related.id;

                    // Find relationships in this record
                    this.extractRelationships(payload, related, type);
                }

                // Many
                else if (relationship.kind == "hasMany") {

                    // Loop through each object
                    related.forEach(function(item, index){

                        // Sideload the object to the payload
                        this.sideloadItem(payload, type, item);

                        // Replace object with ID
                        related[index] = item.id;

                        // Find relationships in this record
                        this.extractRelationships(payload, item, type);
                    }, this);
                }

            }
        }, this);

        return payload;
    },


    /**
     Overrided method
     */
    extractArray: function(store, type, payload, id, requestType) {
        var typeKey = type.typeKey,
            typeKeyPlural = typeKey.pluralize(),
            newPayload = {};

        newPayload[typeKeyPlural] = payload;

        payload = newPayload;

        // Many items (findMany, findAll)
        if (typeof payload[typeKeyPlural] != "undefined"){
            payload[typeKeyPlural].forEach(function(item, index){
                this.extractRelationships(payload, item, type);
            }, this);
        }


        for(var key in payload) {
            if(key === typeKeyPlural) {
                for(var i =0; i < payload[key].length; i++) {
                    if(typeof payload[key][i] !== 'object') {
                        delete payload[key][i];
                    }
                }

            }
        }

        console.log(payload);

        return this._super(store, type, payload, id, requestType);
    },

    extractSingle: function (store, type, payload, id, requestType) {
        var typeKey = type.typeKey,
            typeKeyPlural = typeKey.pluralize(),
            newPayload = {};

        if(!payload[typeKey]) {
            newPayload[typeKey] = payload;
            payload = newPayload;


            if (typeof payload[typeKey] != "undefined"){
                this.extractRelationships(payload, payload[typeKey], type);

                delete payload[typeKeyPlural];
            }
        }

        return this._super(store, type, payload, id, requestType);
    }
});
Martijn
  • 373
  • 1
  • 4
  • 15
  • 1
    You could also take a look at http://sanestack.com/ which implements https://github.com/mphasize/sails-generate-ember-blueprints automatically for you and sets up the whole backend and frontend. – Markus Jan 11 '15 at 00:57