Reputation: 213
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 :-
angular-history doesnt suit my need because I want to watch all attributes of object, which may also contain other objects and array.
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
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
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
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
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
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