Skip to content

Instantly share code, notes, and snippets.

@betocantu93
Created April 7, 2020 19:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save betocantu93/a40daa515cbd829df159a97c233c8826 to your computer and use it in GitHub Desktop.
Save betocantu93/a40daa515cbd829df159a97c233c8826 to your computer and use it in GitHub Desktop.
import Mixin from '@ember/object/mixin';
import Ember from 'ember';
import { singularize } from 'ember-inflector';
export default Mixin.create({
// ember temporary ids
_tempIds: {},
//************************
//***REQUEST SECTION******
//************************
//#4 serialize a related record, example: if creating a visit this will serialize each associated report
//this is where the actual relationship object get built and added to the json
serializeRecord(obj) {
if (!obj) { return null;}
//build the instance
const relatedRecord = obj.serialize({__isSaveRelationshipsMixinCallback: true});
if (obj.id) {
relatedRecord.data.id = obj.id;
this.get('_tempIds')[obj.id] = {};
} else {
relatedRecord.data.id = ''; //required in jsonapi
if (!relatedRecord.data.attributes) { //default hash
relatedRecord.data.attributes = {};
}
relatedRecord.data.attributes['emberid'] = obj.record.get('_internalModel')[Ember.GUID_KEY]; //set temporary id
this.get('_tempIds')[relatedRecord.data.attributes['emberid']] = {}; //store temporary in hash
}
// verify that relatedRecord does not have a relationship with a name present in _tempIds
// TODO this might not be necessary
for (let relationshipName in relatedRecord.data.relationships) {
if (this.get('_tempIds')[relationshipName]){
alert('this is necessary');
delete relatedRecord.data.relationships[relationshipName];
}
}
//remove relationships key if none present
if (relatedRecord.data.relationships === {}){
delete relatedRecord.data.relationships;
}
return relatedRecord.data;
},
// #3 function that modifies the serialization of the snapshot to include its relationships in the json
serializeRelationship(snapshot, data, rel) {
const relKind = rel.kind;
const relKey = rel.key;
//only if serialize: true is specified in the serializer
if (data && this.get(`attrs.${relKey}.serialize`) === true) {
data.relationships = data.relationships || {};
const key = this.keyForRelationship(relKey, relKind, 'serialize');
const relationship = data.relationships[key] = data.relationships[key] || {};
if (relKind === "belongsTo") {
relationship.data = this.serializeRecord(snapshot.belongsTo(relKey)); //add serialized relationship record
} else if (relKind === "hasMany") {
relationship.data = []; // provide a default empty value
const hasMany = snapshot.hasMany(relKey);
if (hasMany !== undefined) {
relationship.data = hasMany.map(this.serializeRecord.bind(this)); //add each serialized relationship record
}
}
}
},
//#2 ember data hook for hasMany relationships (runs for every hasMany relationship in model)
serializeHasMany() {
this._super(...arguments);
this.serializeRelationship(...arguments);
},
//#2 ember data hook for belongsTo relationships (runs for every belongsTo relationship in model)
serializeBelongsTo() {
this._super(...arguments);
this.serializeRelationship(...arguments);
},
// #1 to run, runs only once
serialize (snapshot, options) {
if (!(options && options.__isSaveRelationshipsMixinCallback)) {
this.set("_tempIds", {});
}
return this._super(...arguments);
},
//************************
//***RESPONSE SECTION*****
//************************
// last method to run, removes uncommitted model item so it is not duplicated
updateRelatedRecord(json, store) {
if (json.attributes !== undefined && json.attributes['emberid'] !== undefined){
json.type = singularize(json.type);
const record = store.peekAll(json.type)
.filterBy('currentState.stateName', "root.loaded.created.uncommitted")
.findBy('_internalModel.' + Ember.GUID_KEY, json.attributes['emberid']);
if(record) {
record.rollbackAttributes();
}
}
return json;
},
// #1 ember data hook used to normalize the server response
normalizeSaveResponse(store, primaryModelClass, payload, id, requestType) {
requestType;
const rels = payload.data.relationships || [];
let included = {}; //hash to store jsonapi included section
//convert included array into a hash {model3: {id: 3..}, model170: {id: 170...} ...}
if (payload.included){
included = payload.included.reduce((prev, current) => {
prev[`${current.type}${current.id}`] = current;
return prev;
}, {});
}
Object.keys(rels).forEach(relName => {
let relationshipData = rels[relName].data;
if (relationshipData){
this.normalizeRelationship(relationshipData, store, included);
}
});
// run through the included objects looking for client ids
if (payload.included) {
for(let includedItem of payload.included) {
this.updateRelatedRecord(includedItem, store);
}
}
return this._super(...arguments);
},
//normalizeRelationshipItem and normalizeRelationship are only used to call updateRelatedRecord
//when the relationship data is returned in the 'relationships' section of jsonapi response
//instead of in the 'included' section
// normalize relationships for belongsTo and hasMany
normalizeRelationship(relationshipData, store, included) {
if (Array.isArray(relationshipData)) {
// hasMany
relationshipData = relationshipData.map(item => this.normalizeRelationshipItem(item, store, included));
} else if (relationshipData) {
this.normalizeRelationshipItem(relationshipData, store, included);
}
},
//adds __normalized flag and calls updateRelatedRecord if data is not in 'included' section of response
normalizeRelationshipItem(item, store, included) {
if (item.__normalized) { return; } // skip if already normalized
item.__normalized = true;
let includedData = included[`${item.type}${item.id}`];
if (includedData){
item = includedData;
}
let internalRelationships = item.relationships;
if (internalRelationships !== undefined) { //go recursive
Object.keys(internalRelationships).forEach(relName => {
let relationshipData = internalRelationships[relName].data;
if (relationshipData){
this.normalizeRelationship(relationshipData, store, included);
}
});
}
if (!includedData) {
// if it's in the included block then it will be updated at the end of normalizeSaveResponse
this.updateRelatedRecord(item, store);
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment