2
votes

I am wondering about the best practice for creating a new record in Ember with createRecord() and then persisting it to the API? Specifically, should Ember's POST request generally be a single JSON that embeds all the model's relationships, or is it customary to POST each relationship individually?

In my code, I'm not able to get a single JSON, so I'm wondering if I'm missing the "Ember Way" or (more likely) I have a mistake in my code?

DETAILS:

Here are the details of my setup. I have two models:

/models/OrgUser.js:

DS.Model.extend({
    ...
    orgPerson: DS.belongsTo('org-person', { inverse: 'org-user', async: true, embedded: 'always' }),
});

/models/OrgPerson.js:

DS.Model.extend({
   ...
   orgUser: DS.belongsTo('org-user'),
})

I'm attempting to create a new user on the "Create New User" page. The route for that page is below. Is this the best place to call createRecord() for my new models?

/routes/org-users/add.js:

Ember.Route.extend({
    model: function() {
        var orgPerson = this.store.createRecord('org-person');
        var orgUser = this.store.createRecord('org-user' );
        orgUser.set('orgPerson', orgPerson);

        return orgUser;
    },
     ...
}

Using Chrome console to look at the orgUser object after I call set shows no evidence at all that I have added anything to orgUser. The "Ember" tab of Chrome Debug Tools does reflect the relationship, though.

On my "Create New User" page, my input fields all correspond to both OrgUser properties and OrgUser.OrgPerson properties. Here's an example:

/templates/org-users/add.hbs

...
{{input value=username}} // a property of OrgUser
{{input value=orgPerson.firstName}} // a property of OrgUser.orgPerson
...

In my route, when I go to save() Ember Data POSTs only the orgUser JSON with a null value for orgPerson. I'd like it to embed the orgPerson serialized object in the orgPerson property.

/routes/org-users/add.js:

Ember.Route.extend({
    ...
    actions: {
        submitForm: function() {
            ...
            this.currentModel.save().then( onSuccess ).catch( onFailure );
            ...
        }
    }
});

This results in a POST request with the following body:

{
"orgUser":{
   "username":"MyUsername",
   "orgPerson":null,
   "id":null
}

Note that orgPerson is null. Thanks in advance for any assistance!

UPDATE: Once again, I think I will need to take a fresh look at my serializer. Here's how it's currently defined.

/serializers/application.js:

DS.RESTSerializer.extend({

    // Use the default approach to serializing, but add the id property
    serialize: function(record, options) {
        var json = this._super.apply(this, arguments);
        json.id = record.id;
        return json;
    },

    serializeBelongsTo: function(record, json, relationship) {
    var key = relationship.key;
      key = this.keyForRelationship ? this.keyForRelationship(key, 'belongsTo') : key;

      var data = record.get('data');

      if (relationship.options.embedded && relationship.options.embedded === 'always') {
        json[key] = data[relationship.key] ? data[relationship.key].serialize( { includeId: true } ) : null;
        }
      else {
            json[key] = data[relationship.key] ? data[relationship.key].get('id') : null;
        }

        if (relationship.options.polymorphic) {
            this.serializePolymorphicType(record, json, relationship);
        }
    }
});

Per @Kingpin2k's comment, there appears to be some ambiguity (and bugs!) on how best to handle serialize() for a belongsTo relationship. My serializer customization above works great for records that are obtained through this.store.find(), but now I need to enable them for createRecord(). Additional suggestions, pointers are welcome!

2

2 Answers

1
votes

It's a bug. https://github.com/emberjs/data/issues/1542#issuecomment-49443496

A workaround is to get the async belongsTo record before attempting to save (It tricks Ember Data into initializing it). In your case you could do it in the model hook.

model: function() {
    var orgPerson = this.store.createRecord('org-person');
    var orgUser = this.store.createRecord('org-user');
    orgUser.set('orgPerson', orgPerson);
    return orgUser.get('orgPerson').then(function(){
      return orgUser;
    });
},
0
votes

So, I finally figured this out. With the release of Ember-Data-1.0.0-Beta.9, http://emberjs.com/blog/2014/08/18/ember-data-1-0-beta-9-released.html, the EmbeddedRecordsMixin has been introduced. This pretty much solves all my issues!

So, I wound up doing the following:

This wound up working perfectly, because I get full declarative control over how and when my records are embedded.

Special thanks to @Kingpin2k for helping me realize my serializer was the problem and for the discussion to help me understand the options.