Skip to content

RallyPointNetworks/ember-data-change-tracker

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ember-data-change-tracker

Build Status Ember Observer Score npm version

New

  • By popular demand, added manual mode
    • Manual mode ( which is the default )
      • Auto tracking is turned off
      • Nothing happens until you tell a model to start tracking
    • Auto track mode

This addon aims to fill in the gaps in the change tracking / rollback that ember data does now.

  • Currently ember-data

  • tracks changes for numbers/strings/date/boolean attributes

  • has a changeAttributes() method to see what changed => [ last, current ]

  • has a rollbackAttributes() method to rollback attributes

  • This addon:

    • tracks modifications in attributes that are object/json/custom type
    • tracks replacement of belongsTo associations
    • tracks replacement/changes in hasMany associations
    • adds a changed() method to DS.Model
    • adds a rollback() method to DS.Model
    • Only works with
      • ember-data versions 2.7+ ( if you have polymphic relationships )
      • ember-data versions 2.5+ ( if you don't )
    • Can be used in two modes
      • auto track mode
      • manual track mode ( the default )
    • Uses no observers, no computed properties and no gluten

Installation

  • ember install ember-data-change-tracker

Why?

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.

Changed

  • The method changed() is added to model
  • Shows you any changes in an object attribute type
    • whether modified or replacing the value
    • attr() will default to 'object' type
    • works with any custom type you have created
  • Shows when you replace a belongsTo association
  • Shows when you add to a hasMany association
  • Shows when you delete from a hasMany association
  • Merges ember-data changeAttribute() information into one unified change object
  • Unlike ember-data no last and current value is shown, just the boolean => true
    • Though you will see [last value, current value] for the attributes that ember-data tracks

Example: ( remove from a hasMany )

  user.get('projects').removeObject(firstProject); // remove project1
  user.changed() //=> {projects: true }

Rollback

  • The method rollback() is added to model
  • If you're not using auto track you have to call startTrack() before editing
  • Performace wise, it's way faster than you think it should be.
    • Tested on model with hundreds of items in a hasMany association.
    • Though you might want to think twice when tracking one with thousands

Usage:

  • make and makeList are from ember-data-factory-guy.
    • they create and push models ( based on factories ) into the ember-data store
    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

Configuration

  • Global configuration

    • By default the global settings are:
      • { trackHasMany: true, auto: false }
        • Essentially this says, track everything in the model but only when I tell you
        • Since this is manual mode you probably want to track everything since you are focused on one edit at a time, hence trackHasMany is on
    • The options available are:
      • trackHasMany : should hasMany associations be tracked? ( true is default )
        • this is just a shortcut to exclude all the hasMany relations
      • auto : should tracking be turned on by default? ( false is default )
        • auto tracking means when any model is saved/updated/reloaded the tracker will save the current state, allowing you to rollback anytime
  • Model configuration

    • Takes precedence over global
      • So, globally auto track could be off, but on one model you can turn it on
    • The options available are:
      • trackHasMany : same as global trackHasMany
      • auto : same as global auto
      • only : limit the attributes/associations tracked on this model to just these
      • except : don't include these attributes/associations
      • You can use 'only' and 'except' at the same time, but you could also clean your nose with a pipe cleaner
  // file config/environment.js
  var ENV = {
    modulePrefix: 'dummy',
    environment: environment,
    rootURL: '/',
    locationType: 'auto',
    changeTracker: { trackHasMany: true, auto: true }, 
    EmberENV: {
    ... rest of config
  • Set options on the model
  // 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 })
  });

Serializer extras

  • Mixin is provided that will allow you to remove any attributes/associations that did not change from the serialized json
  • Useful when you want to reduce the size of a json payload
  • removing unchanged values can be big reduction at times

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

Extra's

  • Adds a few more helpful methods to ember data model
    • didChange(key)
      • did the value on this key change?
    • savedTrackerValue(key)
      • this is the value that the key had after it was created/saved and before any modifications

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    

Known Issues

  • Ember less than 2.10 AND you are using auto track mode
  • When pushing data to the store directly to create a model ( usually done when using websockets .. but same issue if using factory guy) you need to call model.saveChanges() manually after creating that new model
  • Testing
    • In unit / integration tests you have to manually initialize change-tracker if you are testing anything that requires the addon to be enabled

For 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();
  }
});

About

extending ember data to track and rollback changes including objects and associations

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 97.2%
  • HTML 2.8%