2

I'm having the following problem.

In my app I have a screen to make a new Site. But when I save the new site via an action on the controller, the languages-property isn't sent with the POST-request to the server.

The template for adding a new Site is this:

<form class="form-horizontal">
<div class="control-group">
    <label class="control-label" for="name">Name</label>
    <div class="controls">
        {{view Ember.TextField valueBinding="name"}}
    </div>
</div>
<div class="control-group">
    <label class="control-label" for="languages">Languages</label>
    <div class="controls">
        {{view Ember.Select contentBinding="controllers.languages" selectionBinding="languages" optionValuePath="content.id" optionLabelPath="content.description" multiple="true"}}
            </div>
</div>
<div class="form-actions">
    <button {{ action "createSite" }} class="btn btn-primary">Save</button>
</div>

I defined my Store like this:

App.Store = DS.Store.extend({
    revision : 12,
    adapter : DS.RESTAdapter.extend({
        namespace : 'rest'
    })
});

This is my controller:

App.SitesNewController = Em.ObjectController.extend({
    needs: ['languages'],
    name: null,
    languages: null,
    createSite : function() {
        var self = this;
        var name = this.get('name');
        var languages = this.get('languages');
        // Create the new Site model
        var s = App.Site.createRecord({
            name : name
        });
        $.each(languages,function(i,lang) {
            s.get('languages').addObject(lang);
        });

        this.get('store').commit();
    }
});

This is the Site-model

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

Language-model:

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

The POST-request data sent to my server is this:

{
  "site":{"name":"test"}
}

So I miss the language-property. Actually I expect a language_ids property with an array of id's.

When I edit my RESTAdapter-configuration like this:

DS.RESTAdapter.map('App.Site', {
    languages: { embedded: 'always' }
});

Now the POST-request data is:

{
  "site": {
    "name":"test",
    "languages":[{
      "id":2,"description":"English"
    },{
      "id":3,"description":"Deutsch"
    }]
  }
}

The languages are know embedded in the request-data. This is no problem, at the backend I get the id before I save it. But know it expects the language-data to be embedded in the GET-responses also.

What is the way to send just the id's in the POST-data? I want it to be something like this:

{
  "site": {
    "name":"test",
    "languages":[2,3]
  }
}
Willem de Wit
  • 8,604
  • 9
  • 57
  • 90

1 Answers1

6

This answer is largely derived from this other StackOverflow answer.

App.Store = DS.Store.extend({
    revision : 12,
    adapter : DS.RESTAdapter.extend({
        namespace : 'rest',
        serializer: DS.RESTSerializer.extend({
            addHasMany: function (hash, record, key, relationship) {
                var type = record.constructor,
                    name = relationship.key,
                    serializedHasMany = [], 
                    manyArray, embeddedType;

                embeddedType = this.embeddedType(type, name);
                if (embeddedType !== 'always') { return; }

                manyArray = record.get(name);

                manyArray.forEach(function (record) {
                    serializedHasMany.push(record.get('id'));
                }, this);

                hash[this.singularize(key) + '_ids'] = serializedHasMany;
            }

        })

    })
});

For reference, you might review the JSONSerializer, which the RESTSerializer mostly inherits from. The code for what addHasMany does can be found here.

Note that, for the above snippet, the only lines that really differ are the last several. Rather than serializing embedded records, ids are pushed to the hash under the singularized key (I would have used RESTSerializer#keyForHasMany if it didn't have its own check for embedded types.

Community
  • 1
  • 1
Kurt Ruppel
  • 131
  • 3
  • 1
    Thank you very much for your answer, I read the source-code and I totally get it. It works fine now. – Willem de Wit Apr 02 '13 at 07:37
  • Thanks for the killer answer. I've omitted the embeddedType !== 'always' check so I can get both sideloading and multiple hasMany id upload – cyberz Aug 22 '13 at 13:20
  • PS. the code has a little bug, it's not able to correctly map camelcase properties to REST convention (eg. childNodes becomes childNode_ids instead of child_node_ids). A workaround is to modify the following at the end of the serializer object: key = this.keyForAttributeName(type, name); hash[this.singularize(key) + '_ids'] = serializedHasMany; – cyberz Aug 22 '13 at 13:57