2
votes

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]
  }
}
1

1 Answers

6
votes

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.