I’ve posted about this before on various forums as I’ve migrated my app from earlier ember-data 1.x days up to current 2.x stuff. I’ve gotten it to work with various hacks, but each time I go through a migration of ember-data I try and see if I can get it done in a cleaner fashion.
Here’s the scenario:
- The API I’m dealing with is not very REST-compliant.
- The API has a single end-point for save new vs. update (both using POST as the http verb) [incidentally, it determines which is intended based on the primary key of the payload being prefixed with “new” or not…ugly I know]
Now, while the API is kind of crap in those senses, it does have a rather cool feature that it calls auto-updating of transient objects. The concept is that while the user is constructing an object on the client, the server offers the ability to ship the transient object to the server wherein the server will validate in real time and potentially add or update other data elements on your object for you in response to user input. For example, think of a sales order where you add a new product to your cart. You can ship your cart to the server as-is with a special call that will validate the cart for you as well as recalculate sales tax via whatever rules it uses or perhaps it activates a volume discount automatically and adds that to the cart because the additional product crosses a minimum threshold. Or maybe the item you just added can’t be added to your cart for some reason, then it will return an error telling you why on that item. The point is that the user gets immediate feedback on those things as well as having the object validated…but without persisting the object on the server.
The server does this via a special end-point /objectName/get_updates (as opposed to /save when you’re ready to persist the entity).
From Ember’s point-of-view though, it’s really not super different from createRecord or updateRecord (except that in the end, the state of the object really shouldn’t be saved to be accurate). Yet I still need the ability to attach errors to fields and stuff like that.
Here’s my latest iteration of how I’m doing this and would love feedback on perhaps a more idiomatically Ember way of doing this as this still seems rather hacky to me:
-
I created an initializer to re-open the base model class and added:
initialize: function(/* container, app */) { DS.Model.reopen({ //see base save function for InternalModel autoUpdate: function(options) { var promiseLabel = "DS: Model#autoUpdate " + this; var resolver = Ember.RSVP.defer(promiseLabel);
this.store.scheduleAutoUpdate(this.model._internalModel, resolver, options); return resolver.promise;
} }); }
-
Then I added analogs to scheduleSave and flushPendingSave on an initializer that re-opens the store:
scheduleAutoUpdate: function(internalModel, resolver, options) { var snapshot = internalModel.createSnapshot(options); internalModel.flushChangedAttributes(); internalModel.adapterWillCommit();
this.autoUpdateContext = { snapshot: snapshot, resolver: resolver }; once(this, 'flushPendingAutoUpdate');
},
//see store.flushPendingSave flushPendingAutoUpdate: function() { var pendingUpdate = this.autoUpdateContext; this.autoUpdateContext = {};
var snapshot = pendingUpdate.snapshot; var resolver = pendingUpdate.resolver; var record = snapshot._internalModel; if(record.isLoaded() === false || record.hasDirtyAttributes() === false) { return; } var adapter = this.adapterFor(record.type.modelName); resolver.resolve(_commit(adapter, this, 'autoupdateRecord', snapshot));
},
I then have to copy the _commit function from the store since it’s not exposed (maybe there’s a way to get at it but I couldn’t figure it out if there is). I’ve also added the appropriate buildurl and operation specific functions on the adapter to handle this.
It seems like if I just had a way to control which operation was chosen as opposed to the base flushPendingSave automatically doing so based on the record’s current state I’d be golden.
Is there a better way to do this or a way that ember-data could be abstracted a bit more in this area to allow me to customize the behavior without having to do so much copy & paste?