Last active
January 1, 2016 08:09
-
-
Save runspired/8116167 to your computer and use it in GitHub Desktop.
A method of saving / updating embedded records for ember.js and ember-data. This is probably NOT the best method, it's simply the best method I've come up with currently "as an adapter". It relies on calls to several store methods that should be private, and essentially turns a single save request into a save queue. This code could be altered to…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var DSA = {}; | |
DSA.RESTAdapter = DS.RESTAdapter.extend({ | |
/* | |
@private | |
Saves a newly created record, traverses and saves dirty related records | |
Currently Only belongsTo relationships are traversed | |
*/ | |
saveRecord : function(_store,type,record,initiator) { | |
var _this = this , | |
//save a single record, bind to parent following | |
save = function(store, recordMap ) { | |
//add parent ID if necessary | |
if( !!recordMap.parent ) { | |
var parentKey = getParentKey( recordMap ); | |
//by setting this end of the belongsTo we set both ends of the belongsTo | |
recordMap.record.set( parentKey , recordMap.parent ); | |
} | |
var data = {} , | |
serializer = store.serializerFor( recordMap.type.typeKey ) | |
; | |
serializer.serializeIntoHash( | |
data, | |
recordMap.type, | |
recordMap.record, | |
{ includeId: true } | |
); | |
return _this.ajax( | |
_this.buildURL(recordMap.type.typeKey) , | |
recordMap.method , | |
{ data: data } | |
); | |
} , | |
//given a recordMap returns the record's key for the parent | |
getParentKey = function( recordMap ) { | |
var matches = []; | |
recordMap.record.eachRelationship(function(r,n){ | |
if( n.kind != 'belongsTo' || !n.type || n.type.typeKey != recordMap.parentType.typeKey ) | |
return; | |
matches.push( n.key ); | |
return; | |
}); | |
Ember.assert( "belongsTo relationship does not exist on both models " | |
+ recordMap.parentType.typeKey | |
+ ' and ' | |
+ recordMap.type.typeKey | |
, matches.length ); | |
//for now only return one match | |
// in the future multiple matches need to be handled and the correct inverse determined | |
return matches[0]; | |
} , | |
//store references to records that have been traversed to avoid | |
// multiple traversals | |
traversedRecords = [] , | |
//iterate 'belongsTo' relationships to find dirty records | |
getRelatedRecords = function(record , parent ) { | |
traversedRecords.push(record); | |
var relatedRecords = []; | |
//fill the relatedRecords cache with dirty belongsTo records | |
record.eachRelationship(function(r,n){ | |
//ignore non-belongsTo relationships | |
if( n.kind != 'belongsTo' || !n.type ) | |
return; | |
//usually this will be an existing record | |
var relatedRecord = record.get( n.key ); | |
//but sometimes it will be an ID, if the ID isn't in the store | |
// we can be certain this record hasn't been altered | |
if( relatedRecord == parseInt( relatedRecord) && _store.hasRecordForId(n.type.typeKey , relatedRecord) ) | |
relatedRecord = _store.recordForId(n.type.typeKey , relatedRecord); | |
//we have a record and it isn't the parent | |
if( !!relatedRecord && (!parent || relatedRecord !== parent) ) { | |
var isDirty = relatedRecord.get('isDirty') , | |
isNew = relatedRecord.get('isNew') , | |
isSaving = relatedRecord.get('isSaving') | |
; | |
if( (isDirty || isNew) && !isSaving ) | |
relatedRecords.push({ | |
attribute : n.key , | |
type : n.type , | |
kind : n.kind , | |
method : isNew? 'POST': 'PUT' , | |
parent : record , | |
parentType : n.parentType , | |
record : relatedRecord | |
}); | |
//get related records for this record | |
if( (initiator == 'update' || (initiator == 'create' && ((isDirty || isNew) && !isSaving))) && traversedRecords.indexOf(relatedRecord) === -1 ) { | |
relatedRecords.concat( getRelatedRecords( relatedRecord, record ) ); | |
} | |
} | |
return; | |
}); | |
return relatedRecords; | |
} , | |
originalRecordMap = { | |
attribute : null , | |
type : type , | |
kind : null , | |
method : (initiator == 'create')? 'POST' : 'PUT' , | |
parent : null , | |
record : record | |
} , | |
promiseChain = getRelatedRecords( record ) , | |
errorSavingChain = function(e) { | |
Ember.Logger.debug('error saving all promises, currently this means only the top most record may have saved and callbacks will still be triggered'); | |
return finalCleanup(); | |
} , | |
chainLink = function(previous, recordMap) { | |
recordMap.record.adapterWillCommit(); | |
recordMap.record._inFlightAttributes = recordMap.record._attributes; | |
recordMap.record._attributes = {}; | |
return previous | |
.then( function(){ return save( _store , recordMap ); } ) | |
.then( | |
function(saveResult){ | |
var serializer = _store.serializerFor( recordMap.type.typeKey ) , | |
serialized = serializer.extract(_store,recordMap.type, saveResult , Ember.get( recordMap.record , 'id' ) , (recordMap.method == 'POST' ? 'createRecord' : 'updateRecord') ); | |
_store.didSaveRecord( recordMap.record , serialized ); | |
_store.updateId( recordMap.record , serialized ); | |
recordMap.record.adapterDidCommit( serialized ); | |
} , | |
errorSavingChain | |
); | |
} , | |
//returned data | |
initialSaveData , | |
//we may want to hook in here and do some stuff later on | |
finalCleanup = function(){ | |
return new Ember.RSVP.Promise( | |
function(resolve, reject) { | |
Ember.run(null, resolve, initialSaveData); | |
}); | |
} , | |
initialLink = save( _store , originalRecordMap ) | |
.then( | |
function(saveResult){ | |
//set initial data | |
initialSaveData = saveResult | |
var serializer = _store.serializerFor( originalRecordMap.type.typeKey ) | |
, serialized = serializer.extract( | |
_store, | |
originalRecordMap.type, | |
saveResult , | |
Ember.get( originalRecordMap.record , 'id' ) , | |
(originalRecordMap.method == 'POST' ? 'createRecord' : 'updateRecord') | |
); | |
_store.didSaveRecord( originalRecordMap.record , serialized ); | |
_store.updateId( originalRecordMap.record , serialized ); | |
originalRecordMap.record.adapterDidCommit( serialized ); | |
} , | |
function(e){ | |
Ember.Logger.debug('an error occurred during the initial save',e); | |
return e; | |
} | |
) , | |
chain = promiseChain.reduce( chainLink , initialLink ) | |
; | |
return chain.then( finalCleanup ); | |
} | |
, createRecord : function(_store,type,record){ return this.saveRecord(_store,type,record,'create'); } | |
, updateRecord : function(_store,type,record){ return this.saveRecord(_store,type,record,'update'); } | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment