Reputation: 488
I'm using ember-data for a project of mine and I have a question that revolves around the possibility of dirtying an object and then setting its state to clean again on purpose - without commiting the changes. The scenario is this:
Say I've fetched an object via banana = App.Fruit.find('banana');
and it has a description of "Yellow fruit!". Using XHR long-polling (or WebSockets), I may receive an updated version of the object because of another user having changed the description to "A tasty yellow fruit!" at any given point in time after I fetched the original object.
Then, what I would like to do is to update the object to reflect the newly received data. For this, I've tried different approaches:
I've tried calling App.Store.load(App.Fruit, new_data);
. First of all, this approach doesn't work and secondly, this is not really what I want. I could've made uncommitted changes to the object myself and in this case, it would be undesirable to just discard those (assuming the load()
call would overwrite them).
I've tried looping through the new data, calling .set()
- like so: banana.set('description', new_data.description);
- in order to update the object properties with the new data (where applicable = not dirty). This works but it leaves the object in a dirtied state.
In order to make the object clean/updated again - and not have the adapter commit the changes! - I've taken a look at the states the object travels through. These are (at least):
rootState.loaded.saved
state..set()
on a property pushes it to the rootState.loaded.updated.uncommitted
state.App.store.commit();
returns the object to the rootState.loaded.saved
state.Therefore, I've tried to manually set the object state to saved
after step 2 like so: banana.get('stateManager').goToState('saved');
.
However, this doesn't work. The next time the store commits for any other reason, this maneuver produces an inFlightDirtyReasons is undefined error.
My question is: how can I manually change the state of a dirtied object back to clean (saved) again?
Upvotes: 24
Views: 13135
Reputation: 431
Tested on Ember Data 3.8.0
Just an update to Martin Malinda's answer:
// Clear changed attributes list
record._internalModel._recordData._attributes = {};
// Trigger transition to 'loaded.saved' state
record.send('pushedData');
In my case I also needed to override serializer's normalize
method.
Upvotes: 1
Reputation: 121
I work on Ember data 1.13 so I used the following solution (which seems a mix between the one provided by @Martin Malinda and the other by @Serge):
// Ensure you have the changes inside the record
Object.assign(record.data, record._internalModel._attributes);
Object.assign(record._internalModel._data,record._internalModel._attributes);
// Using the DS.State you can first simulate the record is going to be saved
record.get('_internalModel').send('willCommit');
// Cleaning the prevous dirty attributes
record.get('_internalModel')._attributes = {};
// Mark the record as saved (root.loaded.created.uncommitted) even if it isn't for real
record.get('_internalModel').send('didCommit');
In this way, if we will call a further rollbackAttributes()
on this record, if we will have some dirty attributes, the record will be reset to this last state (instead of having the original properties) which was exactly what I was looking for in my use case.
If we won't have any dirty attributes, nothing will change and we will keep the last attributes set using this code without having them rolled back to the original ones. Hope it helps.
Upvotes: 1
Reputation: 1608
Tested on Ember Data 2.9
pushedData action is the way to go but besides that the "originalValues" need to be reset as well.
Ember.assign(record.data, record._internalModel._attributes);
Ember.assign(record._internalModel._data, record._internalModel._attributes);
record.send('pushedData');
Upvotes: 2
Reputation: 333
It's an update to @Kamil-j's solution.
For Ember Data 2.0 which I am currently using I have to do the following:
record._internalModel.send('willCommit');
record._internalModel._attributes = {};
record._internalModel.send('didCommit');
Upvotes: 7
Reputation: 699
Solution for Ember Data 2.6.1
record.send('pushedData');
set dirty record as loaded and saved
Upvotes: 10
Reputation: 181
Another method that worked for me when using Ember Data 1.0.0-beta.18:
record.rollback()
This reversed the dirty attributes and returned the record to a clean state.
Seems like this may have been since deprecated in favor of record.rollbackAttributes
: http://emberjs.com/api/data/classes/DS.Model.html#method_rollbackAttributes
Upvotes: 1
Reputation: 51
As of ember-data
1.0.0-beta.12:
record.transitionTo('loaded.saved');
It seems that record.get('stateManager')
is not required anymore.
Upvotes: 5
Reputation: 41
Here's what seems to work for Ember Data 1.0.0-beta.10:
record.set('currentState.stateName', 'root.loaded.saved');
record.adapterWillCommit();
record.adapterDidCommit();
record.set('currentState.isDirty', false);
Not sure if all those lines are required but just following what others have done prior to this.
Upvotes: 4
Reputation: 383
Solution for Ember Data 1.0.0-beta.7:
// changing to loaded.updated.inFlight, which has "didCommit"
record.send('willCommit');
// clear array of changed (dirty) model attributes
record.set('_attributes', {});
// changing to loaded.saved (hooks didCommit event in "inFlight" state)
record.send('didCommit');
I've searched the source code of Ember-data and I've found that loaded.saved state has a setup function that checks whether a model is clean, before setting "saved" state. If it is not clean, then it rejects a request to change state and returns to loaded.updated.uncommitted.
So you have to clean model._attributes array, which keeps attributes names and Ember will let you change state manually.
I know it isn't very good solution, because is needed to set private property of a model, but I've not found any other solutions yet.
Upvotes: 24
Reputation: 1969
It looks like with newer versions everything methioned here got broken.
This worked for me with ember-data 1.0.0.beta4:
record.adapterWillCommit();
record.adapterDidCommit();
Upvotes: 1
Reputation: 9350
As of 1.0.0.rc6.2....
This will move a model into the state of a model that has been saved.
record.get('stateManager').transitionTo('loaded.saved')
This will moves a model to a the state of a new model that has not been committed. Think new dirty model.
record.get('stateManager').transitionTo('loaded.created.uncommitted')
This will move a model into the sate of an old model that has been updated, think old dirty model:
record.get('stateManager').transitionTo('loaded.updated')
Upvotes: 5
Reputation: 250
Looking at ember-data the uncommitted state has a 'becameClean' event which consequently sets the record as loaded.saved.
This should do the trick
record.get('stateManager').send('becameClean');
Upvotes: 12