How to track and rollback relationships?


#1

We’ve recently upgraded from a much much older version of ember-data. I noticed that more modern versions of ember-data don’t consider relationship (belongsTo/hasMany) changes as dirtying the model. It’s also not possible to rollback relationship changes. See: http://emberjs.jsbin.com/jokoxegilonu/3/edit

I’m curious what the recommended way to handle this is? If I have an edit screen and a user changes a relationship, how should I detect if it’s dirty? And how do I rollback the changes if the user clicks cancel?

I’ve seen some older threads about this in this discuss, but none from this year and all seem to suggest you can’t do that. It seems like a pretty straight forward use case.


#2

This is the exact same problem I’ve noticed while working with ember-date. For my specific problem I have a model (i.e., user) which has a belongsTo relationship to another model (i.e., team). If I was editing a user’s attributes (i.e., name), and its relationships (i.e., change to another team). I would expect a user.rollback(); to revert the user’s attributes (i.e., the name, which it does correctly), and any changed relationships (i.e., the team, which it does not).

After looking around it seems that ember-data’s rollback(); does not deal with reverting any of the relationships. I’m wondering if there is a way to achieve the behaviour I had expected?


I had also found an alternative solution, which takes a manual approach to dealing with the relationship management. What I end up doing is creating a new virtual variable on the controller for the user (i.e., teamSelected), which has the value of the user’s original team. Then within the template for editing the user I would use this new variable for the select element. Finally on the controller’s cancel action I simply call rollback(); and the original value remains untouched. As for the save action on the controller, I have to set the new variable’s value (i.e., the new team), on the the original variable (i.e., user.set('team', user.get('teamSelection'));. Not the simplest way to do this, but it works.


#3

I ended up using a “BufferProxy” technique to track belongsTo changes.

IIRC @krisselden had a gist about this at one point. But @bantic has a nice writeup: http://coryforsyth.com/2013/06/27/ember-buffered-proxy-and-method-missing/ and here is a jsbin: http://jsbin.com/uzizip/13

All that said, it’s still very frustrating that ember-data can’t do this. It feels like a really common scenario for anyone who has editable relationships.


#4

I have just done something similar but for a hasMany, I’ve put up the basics here in case it helps anyone else out: https://gist.github.com/kevinansfield/006e403e9429cb04e608

I’m definitely interested to see if there are better solutions out there.

@workmanw: That “BufferProxy” looks interesting, have you run into any issues with it?


Ember js cloning record with relations
#5

I’ve only used the BufferProxy for belongsTo relationships and I haven’t had any issues.

So I’ve not used it for hasMany … but you could probably retro fit it to work with an Ember.ArrayProxy.


#6

@workmanw: Can you post an example of how you used BufferedProxy for belongTo relationships? I’m trying to do the same.

I’ve a simple ember-cli test app https://github.com/ritesh83/BufferedProxyTest.

The ‘title’ field does not update which is what I want according to BufferedProxy but the comment’s text does update. The ‘setUnknownProperty’ method in BufferedProxy doesn’t get called when the comment’s text is modified.


#7

Just find one tricky way to do this.

For example, I have model ‘ball’, which has DS.many relation ships with ‘color’. So you can do as following to rollback all the attributes:

this.get('model.ball').rollbackAttributes();
this.get('model.ball.colors').toArray().forEach(color => color.rollbackAttributes());

Note that .rollbackAttributes() only works on DS.model objects, so what you need to do is to find the DS.model object for the relationship objects. As far as I know, this.get('model.ball.colors') will return a DS.many object, but this.get('model.ball.colors').toArray() will convert DS.many to an array with DS.model objects lke [DS.model A, DS.model B].


#8

I always use one way binding, So i always know the user change the input or not. :slightly_smiling:


#9

FYI there’s a pull request in progress for getting this functionality back: https://github.com/emberjs/data/pull/3698


#10

I created an addon that resolves this issue. Just call rollbackAttributes() and it will rollback your relationships too:

https://www.npmjs.com/package/ember-rollback-relationships