Dirt tracking with Ember-Data - only save dirty properties

Ok I found another approach with the new snapshot api of Ember Data beta 15.

   serializeAttribute: function(snapshot, json, key, attributes) {
      if ( snapshot.record['_inFlightAttributes'] != null 
           || snapshot.get('isNew') )  {
        return this._super(snapshot, json, key, attributes);
      } else {
        return
      }
    }

The same could be done with relationships I guess via:

http://emberjs.com/api/data/classes/DS.ActiveModelSerializer.html#method_serializeBelongsTo and http://emberjs.com/api/data/classes/DS.ActiveModelSerializer.html#method_serializeBelongsTo

Thanks for your effort @gerrit. Should your second line be like this?

if ( snapshot.record['_inFlightAttributes'][key] != null 

The problem here is that _inFlightAttributes contains only regular attributes, not relationships. In my example above, i am overriding the methods you’ve pointed out to, i am not using _inFlightAttributes explicitly to keep bodies of serialize* methods uniform, but if you look at hasKeyChanged, you will see that this hash is used there, it is just not enough

Here is my revised solution that works with Ember CLI version 0.2.7, Ember version 1.13.0, and Ember Data version 1.13.4

Add the following to your serializer:

serializeAttribute: function(snapshot, json, key, attributes) {
// Check if new record
if (snapshot.get('isNew'))  {
  // Is new
  return this._super(snapshot, json, key, attributes);
}  else {
  // Check if current attribute is in flight
  let attrKey = '_internalModel._inFlightAttributes.'+key;
  if (snapshot.get(attrKey) != null) {
    // Attribute is dirty
    return this._super(snapshot, json, key, attributes);
  } else {
    // Attribute is not dirty, not stored in inFlightAttributes
    // Ignore this attribute
    return;
  }
}

Thanks everyone for your help!

I found a little bit of a cleaner function on the snapshot we can use here, called changedAttributes() …

Also does a check to see if the prop is null, and omits it.

serializeAttribute: function(snapshot, json, key, attributes) {
  if ( snapshot.changedAttributes()[key] || snapshot.record.get('isNew'))  {
    return this._super(snapshot, json, key, attributes);

    Object.keys(json).forEach((k) => {
      if (json[k] === null) {
        json[k] = undefined;
      }
    });
  } else {
    return;
  }
}

Also - anyone here have any issues recently with changes to belongsTo properties not recognized by inFlightAttributes?

I’m confused - nothing after the first return will execute… am I taking crazy pills?

Oops! Must have been a typo.

serializeAttribute: function(snapshot,json,key,attributes) {
  if ( snapshot.changedAttributes()[key] || snapshot.record.get('isNew'))  {
    var t = this._super(snapshot, json, key, attributes);

    Object.keys(json).forEach((k) => {
          if (json[k] === null) {
            json[k] = undefined;
          }
        });

        return t;
      } else {
        return;
      }
}
2 Likes

@chrismllr Any idea how to find the changes in association(belongsTo specifically)?

So there is a serializeBelongsTo hook in the RESTSerializer, along with serializeAttribute. It provides you with the same arguments

You can check out all the options here in the ember data docs for RESTSerializer RESTSerializer - 4.6 - Ember API Documentation

1 Like

I am aware of the serializeBelongsTo hook, but DS model’s changedAttributes method doesn’t keep track of belongsTo model changes. Which means there is no way for me to send the belongsTo attributes only when they change, instead I have to send all belongsTo association.

Assume there is a DS model for post like the one below

Post = DS.Model.extend({
  title: DS.attr('string'),
  author: DS.belongsTo('user')
});

Assume that post one has user with id 1 as author. Now if I change author to user with id 2, changedAttributes methods doesn’t contains details of the belongsTo association changed i.e.

   post = Post.find(1);
   post.changedAttributes(); // Object {}
   user = User.find(2);
   post.set('user', user);
   post.changedAttributes(); // still Object {}

P.S: I dont want the post to keep track changes in any of the user attributes, but I want to know changes in relationships if the Post’s author is pointing to new User model.

@chrismllr Any idea how to get it?

So this opens the can of worms, the hasDirtyAttributes property on a model is not triggered by its relationships (hasMany, belongsTo)

I found this RFC -

https://github.com/emberjs/rfcs/pull/21

But ended building out something similar to the first answer in this stack overflow question, another property on the model called isDeepDirty. Its very old, and needs a bit of tinkering. Hope this helps.

Hi all, I use the ember-data-change-tracker addon to accomplish this. Simple install, activate in model and add the keep only changed mixin to the required model serializer, all outgoing requests will now only send attributes that have changed.

1 Like

Or you can add // app/serializers/post.js

export default DS.JSONAPISerializer.extend({

serializeAttribute(snapshot, json, key, attributes) {
if (snapshot.record.get(‘isNew’) || snapshot.changedAttributes()[key]) { this._super(snapshot, json, key, attributes); } }

}); from Saving Only Dirty Attributes - Ember Igniter

BTW Why this is not in Ember ? As I see model.save(‘becomeDirty’) is deprecated ? It takes me ages to realise that.

BTW2 Is there a chance to observe ‘dirtiness’ of object properties ? export default DS.Model.extend({ visitor:DS.attr() ← observ doesn’t work

ember-data-change-tracker => also tracks dirtiness of object properties, which ember data does not do

1 Like

An updated code for ember 3.28, if any one need it

serializeAttribute(snapshot, json, key, attributes) {   
    if (snapshot.record.get('isNew') || snapshot.changedAttributes()[key]) {
        super.serializeAttribute(snapshot, json, key, attributes);
    }
}