Reputation: 984
I want to make a clone of a model currently being edited.
I've found a couple of ways that almost work. But neither are perfect.
1) model.get('data.attributes')
gets all the attributes except for relationships in camelCase form, generates a new record fine but the relationships are missing of course.
2) model.serialize()
generates a JSON object, with all attributes including relationships. But createRecord
will not handle it well since the object is not camelCased (attributes with underscores like first_name
will not be handled)
After my clone has been created I want to transaction.createRecord(App.Document, myNewModelObject)
change/set a couple of attributes and finally commit()
. Anyone have some insight in how to do this?
Upvotes: 16
Views: 11322
Reputation: 1865
You can use https://github.com/offirgolan/ember-data-copyable. Been using this package for some time now, and it is very much reliable. Some of its features :
Shallow & deep copy an Ember Data model
Shallow & deep copy model relationships
Handles cyclical relationships
Handles custom transforms to create true copies
Overwrite, ignore attributes, and copy objects by reference
Intelligent failure and cleanup
Uses ember-concurrency to allow cancelling a copy task
Upvotes: 1
Reputation: 1
This will also solve my problem
Account = DS.Model.extend({
name: DS.attr('string'),
playlists: DS.hasMany('playList'),
favoriteSong: DS.belongsTo('song')
});
Duplicate = Ember.Object.extend({});
TemporaryRoute = Ember.Route.extend({
model : function(){
var model = this.store.findAll('account');
var json = model.toJSON();
var duplicateModel = Duplicate.create(json);
this.set('duplicateModel', duplicateModel);
return model;
}
});
Upvotes: 0
Reputation: 123
Here is the simple way to clone your Ember Model with relationships. working fine.
Create a Copyable mixin like,
import Ember from 'ember';
export default Ember.Mixin.create(Ember.Copyable, {
copy(deepClone) {
var model = this, attrs = model.toJSON(), class_type = model.constructor;
var root = Ember.String.decamelize(class_type.toString().split(':')[1]);
if(deepClone) {
this.eachRelationship(function(key, relationship){
if (relationship.kind == 'belongsTo') {
attrs[key] = model.get(key).copy(true);
} else if(relationship.kind == 'hasMany' && Ember.isArray(attrs[key])) {
attrs[key].splice(0);
model.get(key).forEach(function(obj) {
attrs[key].addObject(obj.copy(true));
});
}
});
}
return this.store.createRecord(root, attrs);
}
});
Add the mixin in your model,
Note: If you want to clone your child model then, you need to include the mixin in child model as well
USAGE:
Upvotes: 0
Reputation: 809
Most simple way I found:
function cloneModel(model) {
const root = model._internalModel.modelName;
const store = model.get('store');
let attrs = model.toJSON();
attrs.id = `clone-${attrs.id}`;
store.pushPayload({
[root]: attrs
});
return store.peekRecord(root, attrs.id);
}
Upvotes: 1
Reputation: 1377
Now we have a add-on to copy models ember-cli-copyable
With this add on, just add the Copyable
mix-in to the target model which is to be copied and use the copy method
Example from the add-on site
import Copyable from 'ember-cli-copyable';
Account = DS.Model.extend( Copyable, {
name: DS.attr('string'),
playlists: DS.hasMany('playList'),
favoriteSong: DS.belongsTo('song')
});
PlayList = DS.Model.extend( Copyable, {
name: DS.attr('string'),
songs: DS.hasMany('song'),
});
//notice how Song does not extend Copyable
Song = DS.Model.extend({
name: DS.attr('string'),
artist: DS.belongsTo('artist'),
});
//now the model can be copied as below
this.get('currentAccount.id') // => 1
this.get('currentAccount.name') // => 'lazybensch'
this.get('currentAccount.playlists.length') // => 5
this.get('currentAccount.playlists.firstObject.id') // => 1
this.get('currentAccount.favoriteSong.id') // => 1
this.get('currentAccount').copy().then(function(copy) {
copy.get('id') // => 2 (differs from currentAccount)
copy.get('name') // => 'lazybensch'
copy.get('playlists.length') // => 5
copy.get('playlists.firstObject.id') // => 6 (differs from currentAccount)
copy.get('favoriteSong.id') // => 1 (the same object as in currentAccount.favoriteSong)
});
Upvotes: 5
Reputation: 984
Here's an updated answer, it still doesn't handle hasMany
relationships.
cloneBelongsTo: function(fromModel, toModel) {
var relationships;
relationships = Em.get(fromModel.constructor, 'relationships');
return relationships.forEach(function(relationshipType) {
var _relType;
_relType = relationships.get(relationshipType);
return _relType.forEach(function(relationship) {
var name, relModel;
relModel = Em.get(fromModel, relationship.name);
if (relationship.kind === 'belongsTo' && relModel !== null) {
name = relationship.name;
return toModel.set(name, fromModel.get(name));
}
});
});
}
And here's how I use it:
// create a JSON representation of the old model
var newModel = oldModel.toJSON();
// set the properties you want to alter
newModel.public = false;
// create a new record
newDocument = store.createRecord('document', newModel);
// call the cloneBelongsTo method after the record is created
cloneBelongsTo(model, newDocument);
// finally save the new model
newDocument.save();
Upvotes: -1
Reputation: 47
How about using toJSON() method instead of serialize() like this
js
transaction.createRecord(App.Document, model.toJSON());
Upvotes: 3