extending ember data to track and rollback changes including objects and associations
New
This addon aims to fill in the gaps in the change tracking / rollback that ember data does now.
Currently ember-data
changedAttributes()
method to see what changed => [ last, current ]rollbackAttributes()
method to rollback attributeshasDirtyAttributes
computed propertyThis addon:
modelChanges()
method to DS.Modelrollback()
method to DS.ModelisDirty
computed property to DS.Model ( only if enabled in configuration )hasDirtyRelations
computed property to DS.Model ( only if enabled in configuration )ember install ember-data-change-tracker
Say there is a user model like this:
export default Model.extend({
name: attr('string'), // ember-data tracks this already
info: attr('object'), // ember-data does not track modifications
json: attr(), // ember-data does not track modifications if this is object
company: belongsTo('company', { async: false, polymorphic: true }), // ember-data does not track replacement
profile: belongsTo('profile', { async: true }), // ember-data does not track replacement
projects: hasMany('project', { async: false }), // ember-data does not track additions/deletions
pets: hasMany('pet', { async: true, polymorphic: true }) // ember-data does not track additions/deletions
});
You can not currently rollback the info, json if they are modified or company, profile, projects and pets if they change.
modelChanges()
is added to modelchangeAttribute()
information into one unified change objectExample: ( remove from a hasMany )
user.get('projects').removeObject(firstProject); // remove project1
user.modelChanges() //=> {projects: true }
rollback()
is added to modelstartTrack()
before editingUsage:
let info = {foo: 1};
let projects = makeList('project', 2);
let [project1] = projects;
let pets = makeList('cat', 4);
let [cat, cat2] = pets;
let bigCompany = make('big-company');
let smallCompany = make('small-company');
let user = make('user', { profile: profile1, company: bigCompany, pets, projects });
// manual tracking model means you have to explicitly call => startTrack
// to save the current state of things before you edit
user.startTrack();
// edit things
user.setProperties({
'info.foo': 3,
company: smallCompany,
profile: profile2,
projects: [project1],
pets: [cat1, cat2]
});
user.rollback();
// it's all back to the way it was
user.get('info') //=> {foo: 1}
user.get('profile') //=> profile1
user.get('company') //=> bigCompany
user.get('projects') //=> first 2 projects
user.get('pets') //=> back to the same 4 pets
Usage:
let info = {foo: 1};
let pets = makeList('cat', 4);
let [cat, cat2] = pets;
let bigCompany = make('big-company');
let smallCompany = make('small-company');
let user = make('user', { company: bigCompany, pets });
user.startTrack();
// edit things
user.set('name', "new name");
user.get('isDirty'); //=> true
user.rollback();
user.get('isDirty'); //=> false
user.set('company', smallCompany);
user.get('hasDirtyRelations'); //=> true
user.get('isDirty'); //=> true
user.rollback();
user.get('isDirty'); //=> false
user.set('pets', [cat, cat2]);
user.get('hasDirtyRelations'); //=> true
user.get('isDirty'); //=> true
user.rollback();
user.get('isDirty'); //=> false
// things that don't work
user.set('info.foo', 3);
user.get('isDirty'); //=> false ( object/array attributes don't work for computed isDirty )
Global configuration
hasDirtyRelations
for checking on changed relationshipsisDirty
for checking on any changes
Model configuration
// file config/environment.js
var ENV = {
modulePrefix: 'dummy',
environment: environment,
rootURL: '/',
locationType: 'auto',
changeTracker: { trackHasMany: true, auto: true },
EmberENV: {
... rest of config
// file app/models/user.js
export default Model.extend({
changeTracker: {only: ['info', 'company', 'pets']}, // settings for user models
name: attr('string'),
info: attr('object'),
json: attr(),
company: belongsTo('company', { async: false, polymorphic: true }),
profile: belongsTo('profile', { async: true }),
projects: hasMany('project', { async: false }),
pets: hasMany('pet', { async: true, polymorphic: true })
});
Example:
Let's say you set up the user model's serializer with keep-only-changed mixin
// file: app/serializers/user.js
import DS from 'ember-data';
import keepOnlyChanged from 'ember-data-change-tracker/mixins/keep-only-changed';
export default DS.RESTSerializer.extend(keepOnlyChanged);
Then when you are updating the user model
user.set('info.foo', 1);
user.serialize(); //=> '{ info: {"foo:1"} }'
Without this mixin enabled the json would look like:
{ name: "dude", info: {"foo:1"}, company: "1" companyType: "company", profile: "1" }
where all the attributes and association are included whether they changed or not
didChange(key)
savedTrackerValue(key)
Usage:
user.startTrack(); // saves all keys that are being tracked
user.savedTrackerValue('info') //=> {foo: 1} original value of info
user.set('info.foo', 8)
user.didChange('info') //=> true
user.savedTrackerValue('info') //=> {foo: 1} original value of info
model.saveTrackerChanges()
manually after creating that new modelFor example:
import {moduleForModel, test} from 'ember-qunit';
import {make, manualSetup} from 'ember-data-factory-guy';
import {initializer as changeInitializer} from 'ember-data-change-tracker';
moduleForModel('project', 'Unit | Model | project', {
beforeEach() {
manualSetup(this.container);
changeInitializer();
}
});