Ember-data 1.0 beta 3 parentModel.rollback() does not rolling back children state

I’m using the new Ember-data 1.0.0 beta3 and have this issue (which I did not have with 13 revision!)

Giving two models with hasMany relationship

App.Post = Ember.Model.extend
  title: DS.attr('string')
  images: DS.hasMany('image', async:true)

App.Image = Ember.Model.extend
  title: DS.attr('string')

I have a lot of images in the application and not necessary that one belongs to post. Then, I’m trying to push some images to Post from the controller (does not matter from where is this context)

assignImage: (image_id) ->
  post = @get('controllers.post.content')
  @store.find('image', image_id).then (image) =>
    topic.get('images').addObject(image)
    post.transitionTo('updated.uncommitted');

So far so good, I got a “Cancel” button active because, I have manually sent a Post to “isDirty” state, but the issue is, when I’m doing rollback()

discardChanges: (post) ->
  if(post.get('isDirty'))
    post.rollback()

I’m not getting the state before I have added the image, I’m mean: it is working fine for “title” but not for image_ids

Does any one know if it’s expecting behaviour? And, btw why changing children records is not making parent ‘isDirty’ anymore… Again: with rev13 was everything fine :frowning:

I can’t find where I read it, but I know that part of the decision to deprecate DS.hasOne (which was just an alias for DS.belongsTo), was because that going forward parent-child relationships will dictate when a parent is considered dirty because of its children. However, this is still an outstanding task for the Ember Data team.

For now you’ll need to override isDirty and rollback (and perhaps save) to have the behavior you want:

DS.Model.extend({
  // When the record is fetched, save its relations so they can be reverted
  _saveRelations: function() {
    var savedRelations = this._savedRelations = {};
    this.constructor.eachRelationship(function(key, relationship) {
      if (relationship.kind === 'hasMany') {
        savedRelations[key] = this.get(key).toArray();
      }
    }, this);
  }.on('didLoad', 'didCreate', 'didUpdate'),

  // Rollback relations as well as attributes
  rollback: function() {
    // Revert attributes like normal
    this._super();

    // Revert relationships to their state at last fetch
    if (this._savedRelations) {
      Ember.keys(this._savedRelations).forEach(function(key) {
        this.suspendRelationshipObservers(function() {
          this.get(key).setObjects(this._savedRelations[key]);
        }, this);

        // Rollback child records that have changed as well (optional)
        this.get(key).filterBy('isDirty').invoke('rollback');
      }, this);
    }
  }
});

App.SomeModel = DS.Model.extend({
  // Model is dirty if any child relations have changed
  isDirty: function() {
    return this._super('isDirty') ||
      this._savedRelations && Ember.keys(this._savedRelations).any(function(key) {
        // Deep compare requires Underscore.js
        return !_.isEqual(this._savedRelations[key], this.get(key).toArray()) ||
          this.get(key).anyBy('isDirty');
      }, this);
  }.property('currentState', /* list all relationships keys here with both `.[]` and `.@each.isDirty` */).readOnly()
});

Note: I have not tested this code.

1 Like

Thank you very much for the insight. This is very important part, which was changed without any notice in CHANGELOG.md. Your solution seems the right way to generalise the fix, but for now I will stick to old straightforward way

  1. Save the children before manipulation
  2. Revert them, when hitting “Discard Changes”

At this point, after all pain, I have been through with Ember-data, I wish I have never choose it at first place.

It doesn’t seem like this.suspendRelationshipObservers() exists in DS.Model anymore. Do you know what the alternative is? Thanks!

@johnnyo I haven’t done the deep dive into ED relationships since the last major refactor, so I can’t say. It’s possible that it’s not even necessary anymore.

It’s still necessary and the community is trying to figure out how to get this right:

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

I provided a complete solution on StackOverflow in case anyone is running into this same problem:

@johnnyo Sorry, you asked specifically about this.suspendRelationshipObservers, which I think may not be necessary anymore. Relationship dependent dirty tracking is certainly not supported by Ember Data.

@roma_khudyakov ,

For your information: Model.rollback() is RENAMED to model.rollbackAttributes() https://github.com/emberjs/data/pull/3305