JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.js"></script>
<form id="app" ng-controller="Ctrl" undo-support="">
<input name="name" undoable="" ng-model="data.name" /><br/>
<input name="value" undoable="" ng-model="data.value" /><br/>
<button ng-click="log()">Log</button>
<button ng-click="undo()" ng-disabled="!canUndo">Undo</button>
<button ng-click="redo()" ng-disabled="!canRedo">Redo</button>
<p>
An external change will break the functionality;
here <code>extChange()</code> should create and add a command.
<br/>
<button ng-click="extChange()">External change</button>
</p>
</form>
JavaScript
var app = angular.module("app",[]);
function MyData() {
this.name = null;
this.value = null;
}
app.controller("Ctrl", function($scope) {
angular.extend($scope, {
data: new MyData(),
log: log,
extChange: extChange
});
function log() {
console.log($scope.data);
}
function extChange() {
$scope.data.name = "xxx";
}
});
function NgModelCommand(oldval, newval, applier) {
this.oldval = oldval;
this.newval = newval;
this.applier = applier;
}
NgModelCommand.prototype.execute = function() {
this.applier(this.newval);
};
NgModelCommand.prototype.rollback = function() {
this.applier(this.oldval);
};
app.directive("undoSupport", function() {
return {
controller: function($scope) {
var commands = [], i = -1;
this.add = function(oldval, newval, applier) {
i += 1;
commands[i] = new NgModelCommand(oldval, newval, applier);
if( i < commands.length-1 ) commands.length = i+1;
};
$scope.undo = function() {
if( $scope.canUndo ) {
var cmd = commands[i];
i -= 1;
cmd.rollback();
}
};
$scope.redo = function() {
if( $scope.canRedo ) {
i += 1;
var cmd = commands[i];
cmd.execute();
}
};
$scope.canUndo = false;
$scope.canRedo = false;
$scope.$watch(
function() {
return commands.length > 0 && i >= 0;
},
function(newval) {
$scope.canUndo = newval;
}
);
$scope.$watch(
function() {
return commands.length > 0 && i < commands.length-1;
...