Revert change to ember data model - ember.js

Is there a way to revert a change to an Ember Data model easily?
I have a model bound to an edit view. This view enables the user to cancel editing, at which point I'd like to revert the changes to the model. Is there an easy way to do this without cloning all the values off the side?

Starting from Ember Data version 2, there are not transactions anymore, but you can reset models to their last status before saved editions with:
model.rollbackAttributes();

Ember Data supports the concept of transactions. We can create a transaction and assign Ember data records to them and if we want to undo the changes we can call the transactions rollback() method. Also if we do not create an explicit transaction the records are assigned to a default transaction which can be rolled back by calling the rollback() method on the DS.store object itself.

The name of the default transaction is named 'defaultTransaction'. In fact, when Embers commits, it uses this default transaction. I can't use directly rollback on the store directly.
Instead of using store.rollback, you should have something like:
store.get('defaultTransaction').rollback()
or (if you are in a router event manager)
event.get('store.defaultTransaction').rollback()

You could also do a rollback on the model itself if it is in an "isDirty" state.
this.get('model').rollback();
Example
export default Ember.ObjectController.extend({
actions: {
cancelEditModel: function(){
this.get('model').rollback();
this.transitionToRoute('...');
return false;
}
}
});

Related

can i createRecord() without using save()?

I'm learning ember from the scratch and i stumbled upon something i can't quite understand:
const newNote = this.store.createRecord('note', {
content: 'once upon a time...',
});
newNote.save().then(this._redirectToNewNote.bind(this));
Is the result of createRecord in the database before saving, or is it like a dummy object? can I use createRecord without saving?
Yes. you can use it without saving, do remember it will be removed from store when you refresh page.
From ember guide
createRecord is used for creating new records on the client side. This will return a new record in the created.uncommitted state. In order to persist this record to the backend you will need to call record.save().

Ember.js: Loading related models directly

I dont understand how ember loads related models.
Lets say thats my model:
export default DS.Model.extend({
title: DS.attr('string'),
description: DS.attr('string'),
states: DS.hasMany('state', {async: true})
})
I load this on of my outer routes. When navigating though an ember-app (into nested routes), model-contexts are often provided for routes not by the model-hook of the route but with the link-to helper (when using dynamic-segments, the model-hook will be ignored). When the target route has something in its template like {{#each model.states as |state|}}, ember will load automatically the related model-entrys from (in that case)the state-model. (How and why? - Just because of the each in the template?
When accesing an dynamic-route directly, the model is not given and the model-hook of the dynamic route will be called. So loading my model is easy: just override the model hook and load record with the url parameter (return this.store.find('item', {title: params.item_title})). But no related models will be loaded. How can I do that manually and what (and when) is the way ember do it by default?
How does Ember know to automatically fetch relationships?
ember-data allows you to define relationships (currently only belongsTo and hasMany) with async option set to true or false. Based on this option, after fetching the model from the API (via find method), ember-data will expect relationships object either directly in the response JSON or not. You have async: true (which is rather a common and supported way of handling relationships), therefore ember-data assumes that in your JSON response it gets the ids of states, but not necesseraily the states themselves.
If you define your hasMany as async: true, it always returns promise. It means, that if you make something like this:
this.get("item").get("states")[0]
Will not work, as get("states") will not return an array, but the promise to fetch this array. However, Handlebars are smart (as the get and set methods of Ember) and it can find out what is a promise and wait for it to resolve before using it content. Therefore if your template contains:
{{#each model.states as |state|}}
Handlebars finds out that states is promise, wait for it to resolve, and after resolve they use its content as an array. Very similar behaviour can be found using belongsTo method. Assuming that your item has one state, if you use the code as follows:
this.get("item.state.somePropertyOfState")
Even if you did not fetched the state and currently you don't know what is the somePropertyOfState value, ember get will find out it's a promise and fetch it for you automatically.
How can I manually fetch relationships?
There are couple of ways to do it.
First one is to explicitly fetch them in ember code:
this.get("item.states").then(function(states) {
# now you have fetched the states that the item has, and moreover
# they are accessible in states variable
});
Second, you can let Ember do it automatically for you as I described formerly (e.g. via template).
Third, you can send the relationships with your response using a mechanism called sideload. This will significantly reduce the number of API requests. When you allow ember fetch your relationships, ember is performing one request per one relationship object, which means that if you have ten states that belongs to item, the API will be hit ten times. However, if you sidelod the states while fetching the item, the request will be sent only once. Take a look here to get more info about that.
Sorry for a long post, but I hope that I clarifed a bit.

Ember observer fires on array controller when it shouldn't

Update
Here's a bin of my problem: http://emberjs.jsbin.com/ruwet/8/edit?html,js,console,output4
I have a simple filtered property on an array controller:
import Ember from 'ember';
export default Ember.ArrayController.extend({
activeEncodes: function() {
return this.filterBy('model', 'isActive');
}.property('model.#each.isActive');
});
I'm connected to a websocket and am updating encodes in my store with
store.store.push('encode', data);
Even though it's just updating a certain property on my data (progress), activeEncodes is being recalculated. This is causing some visual jank in my templates.
If I use model.#each.id, it doesn't recalculate, but any other property seems to trigger the CP to recalculate (even if that property is not changing).
Any idea why this is happening?
When you push it causes the entire model to invalidate. This is then considered a new model which fires any observers watching anything on that model (since it sees it as a new model). It's tangentially related to Ember data model reload causes item in {{each}} to be removed/inserted back - losing current state as well.
id is likely a special case, I believe it lives outside of the scope of the underlying properties on the model (same reason you don't define the id on the model). I'm just guessing on that though.

What is the different between Ember-data Store's currentTransaction and defaultTransaction?

General question:
I would like to get an overview of current transaction (currentTransaction) and default transaction (defaultTransaction).
Specific question:
I've been comparing in the Ember controller this.get('model').save() and this.get('store').commit().
this.get('model').save() will eventually call Ember-data Store's get(this, 'currentTransaction').commit()., see github.
this.get('store').commit() will eventually call Ember-data Store's get(this, 'defaultTransaction').commit(), see github.
In the cases of updating a single edited record, they seem to be exactly the same. How should they be used differently?
I would like to get an overview of current transaction (currentTransaction) and default transaction (defaultTransaction)
currentTransaction is for records that were scheduled to be persisted, typically via model.save()
defaultTransaction is for persisting records that were not explicitly added to another transaction
In the cases of updating a single edited record, they seem to be exactly the same. How should they be used differently?
In that case they are going to do the same thing.
In general model.save() is probably a better choice, since 1) it wont have unexpected side-effects if there are other unsaved records and 2) calling save() on multiple models will result in just one commit(), making batch save possible.
Somewhat related:
Difference between model.save() versus model.get('store').commit()

How to undo changes to a model?

Is it possible to revert the model to a clean state? I mean undo all the changes to its properties and mark the state as being clean?
Ember-Data now has an awesome implementation of this. They utilize the transaction class to manage the changes to your objects, which imo is the right place for this. To rollback changes on a model:
Obtain the Model
// if PostController is a ArrayController or ArrayProxy
p = App.PostController.objectAt(0);
Get the object's associated transaction instance and call it's rollback() method.
p.get("transaction").rollback();
From Ember 2.0 this is the way to go (once you have got your model object 'model'):
model.rollbackAttributes();
See https://guides.emberjs.com/v2.0.0/models/creating-updating-and-deleting-records/
There is an open PullRequest by Paul Chavard #tchak, see https://github.com/emberjs/data/pull/176
UPDATE: works with ember version <= 1.13
this.get('model').rollback();
see similar question: Revert change to ember data model