Undo and revert functionality ember data

Does anyone have any clear examples of implementing an “undo” pattern with Ember Data?

Is there a straightforward way to reset the state of a record after it is in the isDirty state? How do people manage the change events and undo history? Is there something built in to handle this? A glance at the API docs does not look like there is. And reload is claimed to not work once a record isDirty.

reload

Reload the record from the adapter.

This will only work if the record has already finished loading and has not yet been modified (isLoaded but not isDirty, or isSaving).

Using RESTful adapter.

I’m not aware of any undo/redo functionality in Ember (Data). There are however numerous third party libraries that do this in Javascript.

A few months ago I created JS-UndoManager which is a Javascript undo manager based on the NSUndoManager you find in Cocoa. It supports grouping and coalescing multiple undo’s. It can probably be adapted to work with Ember (Data).

@eccegordo I suggest looking beyond Ember-Data. The BufferedProxy is likely a useful pattern to implement.

If want multiple-step undo/redo, the proxy is still a good place to start. I think the BufferedProxy could be modified for that usage pretty easily.

Thanks @Rengers and @mixonic, yes the Cocoa NSUndoManager is what I had in the back of my mind. So will check that out.

I had heard the phrase buffered proxy before, just now put it together. Does look like a useful pattern. Thanks for link.

Also, just to confirm my understanding:

Once in ember data a record is marked isDirty=true the only way to change or remove that flag is to

1.) call save and push data to backend API (or localDB)?

2.) Reload the page and lose/reset the existing local state?

So this implies with all the binding going on within ember you are encouraged to “save early, save often” and pushing changes frequently to the backend API, perhaps more technically, the adapter, REST or otherwise.

@mixonic one question. I looked at the Buffered proxy, and it makes sense as a mixin for ObjectController. But one stumbling block that I am running into is with components.

In my app I have an ObjectController, but within my UI, I have a few sub components that I use to delegate parts of the UI, such as forms and editing functionality. This keeps my code relatively clean. I take the model instance and pass it into the different components. And this model is bound together across the different components and various events bubble up to the controller. However, in this scenario the BufferedProxy mixin doesn’t seem to work. I have tried applying the mixin to both the controller and the component as well.

I can get it to work with a vanilla ObjectController but not when components are involved.

Any thoughts? I can try putting together a JSBin example.

Have you looked at the rollback method that undoes any uncommitted changes you have made on a record? I’m not sure how to undo only one change, though.

I had tried the rollback method. But it didn’t work for me as expected. Maybe it was a syntax issue on my part. Also, was a little confused on the status of that feature. I can’t find a reference to it in the API docs. But I do see a reference in the source code. Also the Transition guide mentions that Transactions are gone so I assumed rollback was a related feature. If someone knows a simple functioning example that would help.

https://github.com/emberjs/data/blob/master/packages/ember-data/lib/system/model/model.js#L408

Transactions are indeed “gone” in Ember data, but you can still use rollback on any dirty record:

cancel: function () {
      var author = this.get('model');
      author.rollback();
    }

The rollback is thus on the record and no longer on the transaction.

See also http://discuss.emberjs.com/t/migrating-from-ember-data-0-13-to-1-0-0-beta-1-my-findings for some more info.

Good luck ! Marc

1 Like

Awesome, thanks @cyclomarc that worked. I sometimes forget about the getters. I think I was trying

this.model.rollback();

which wasn’t working, oh that tricky this :smile: