Libu
Libu

Reputation: 213

Undo Redo In Angular JS

I have a big object lined in $rootScope (say > 100 objects and each having hierarchy of objects/array again), I want to $watch entire $rootScope with deepWatching(i.e. turning 3rd parameter of $watch to TRUE).

But the problem here is, $watch returns 2 objects (i.e. one Old RootScope and Modified RootScope). Then I have to do a process of checking what attribute of object changed in $rootScope and its hierarchy to PUSH it into stack.

Do we have an easy way out to get the exact attribute changed while watching a $scope?

$scope.$watch($rootScope, function(oldObj, newObj){

   //all I want here is exactly what attribute changed, NOT entire objects!

}, true);

Alternatively, I could add watch on each attribute of the Object but it appears to be extremely expensive.

What is the best way to achieve undo/redo in angular js?

Note :-

  1. angular-history doesnt suit my need because I want to watch all attributes of object, which may also contain other objects and array.

  2. I'm certain watching entire $rootScope is not a good idea either, but I have aimed to build an UI which has several grids, drag n drop, might contain form, elements can be deleted. So I want to build an overall solution to stack the changes and undo it on CTRL + Z. Imagine replicating desktop version of Photoshop.

Upvotes: 6

Views: 10217

Answers (5)

koppor
koppor

Reputation: 20531

The library angular-history provides undo/redo.

Currently not maintained. Most recent fork seems to be https://github.com/domoritz/angular-history.

Upvotes: 2

mantramantramantra
mantramantramantra

Reputation: 1033

I think the LazyJsonUndoRedo is what you're looking for.

https://github.com/azazdeaz/LazyJsonUndoRedo

From the readme:

A 'drop in' history handler with automatic undo/redo functionality for nested javascript objects, using ES6 Object.observe() or Polymer shim.

http://dailyjs.com/2014/07/18/lazy-json-undo/

Upvotes: 0

Zambezi
Zambezi

Reputation: 777

There's also an undo/redo library called Angular-Chronicle. For myself at least, it was a better option than angular-history.

Upvotes: 4

Nikos Paraskevopoulos
Nikos Paraskevopoulos

Reputation: 40298

Undo/redo is based on the command pattern. And yes, there will be a lot of watches; such a rich functionality does not come cheap.

Just for the fun of it, here is a simple (but quite usable and extensible) implementation: http://jsfiddle.net/sYc4e/1/

A command is an interface for objects that know how to apply and rollback a change:

function XxxCommand() {
    // implementation specific
}
Command.prototype.execute = function() {
    // implementation specific
};
Command.prototype.rollback = function() {
    // implementation specific
};

Usage: You decorate the <input>s with a directive:

<input name="name" undoable ng-model="data.name" />

And wrap them with an element (e.g. the <form>) with the undo-support directive:

<form undo-support>

The link function of undoable listens for a change in the <input> and registers a command with the undoableSupport. The controller of undoableSupport keeps track of the command list.

Upvotes: 5

Vlad Gurovich
Vlad Gurovich

Reputation: 8463

instead of watching the whole $rootScope or for that matter any one object that contains a lot of items which in turn contain a lot of items requiring you to iterate through that nested array on every digest, I would use an array(using it as a queue) of changes that are applied to the original data model and a variable that points to a current location in that queue.

I would put that array and that variable in a service and then change that index whenever undo/redo happens. You can watch that variable from everywhere that you need to respond to these changes, like directives

Upvotes: 0

Related Questions