Angular-Chronicle

An undo/redo library for AngularJS

Download .zip Download .tar.gz View on GitHub

Chronicle

An undo/redo service for AngularJS.

Basic Usage

Full Usage

At the moment, there are only four arguments you can give Chronicle.record.

$scope.chron = Chronicle.record('watchVar', $scope, handleStringsBool, 'noWatchVar');

'watchVar' is the stringified variable you want to be able to undo on.

scope is the variable that contains your watch variable and your unwatched variables.

handleStringsBool is the variable that determines if you want to handle strings in a more user input friendly way.

noWatchVar is the stringified variable that you want to be able to store alongside your watch variable but not trigger your watch variable.

The functions that you can then use on $scope.chron:

$scope.chron.undo();

Undoes to the previous change.

$scope.chron.redo();

Redoes to the next change.

$scope.chron.addOnUndoFunction(fn);
$scope.chron.addOnRedoFunction(fn);
$scope.chron.addOnAdjustFunction(fn);

Use these functions to make Chronicle call a function whenever a significant event happens to the Chronicle object.

$scope.chron.removeOnUndoFunction(fn);
$scope.chron.removeOnRedoFunction(fn);
$scope.chron.removeOnAdjustFunction(fn);

Removes the passed function call on the event. More info on the above 6 functions here.

$scope.chron.canUndo();
$scope.chron.canRedo();

Returns true if the action can be performed, false if you can't perform the action at the current time.

Watch Variable

This parameter must be a string version of how you access the variable you want to watch. For example:

$scope.obj.arrOfObjs[2].foo //The var you want to watch

$scope.chronicle = Chronicle.record('obj.arrOfObjs[2].foo', $scope); //This is valid
$scope.chronicle = Chronicle.record(obj.arrOfObjs[2].foo, $scope); //invalid
var index = 2;
$scope.chronicle = Chronicle.record('obj.arrOfObjs[index].foo', $scope); //invalid

Generally good practice would be to avoid trying to watch a specific variable in an array. However watching a specific variable in an object is just fine.

The watch will record the entire variable. So if the watch variable is an object or array, it will watch everything in the object/array:

$scope.obj = {};
$scope.obj.arr = [1, 2, 3];
$scope.obj.str = 'abc';

$scope.chronicle = Chronicle.record('obj', $scope);
...
$scope.obj.newVar = 123; //New thing to undo
...
$scope.arr[0] = 4; //New thing to undo
...
$scope.str += 'def'; //New thing to undo.

Scope

This is the scope of the controller with the variable you want to watch. $scope can be used.

Controller As Syntax

This library fully supports "controller as" syntax. So everything will work just the same as using $scope, just replace $scope with this:

this.watchThis = 'something'
this.chronicle = Chronicle.record('watchThis', this);

As well as:

var vm = this;
vm.watchThis = {};
vm.chronicle = Chronicle.record('watchThis', vm);

String Handling

This service provides an option for handling strings in a special way. This option was designed with text inputs in mind - no user wants an undo to change only a single character each time. String handling is by default not active.

To enable this, pass true to the third arguement on Chronicle.record.

$scope.undoRedo = Chronicle.record('var', $scope, true); // turns string handling on

Specific Details on String Handling

Specific string handling, when on, works like this: The newest update is always kept. However if the newest update is considered to be too similar to the update before it, it removes the update before it completely. Too similar is defined as:

  • One string contains the other string completely, in order, with only 1 place where the longer string contains extra characters.

  • The longer string is no more than 15 characters longer than the shorter string (this number can be changed - it is the variable MAX_STRING_CHANGE_SIZE at the top of chronicle.js

  • The longer string has no whitespace in the extra characters

  • No Watch Variables

    You have the option to provide secondary variable(s) to Chronicle which will be stored whenever your watch variable is changed but, if changed, will not trigger a new undo location. This looks like so:

    $scope.watchThis = 'abc';
    $scope.dontWatchThis = 'ignored';
    $scope.chron = Chronicle.record('watchThis', $scope, false, 'dontWatchThis');
    
    

    Since the no watch variable option is the fourth one, be sure to pass the string handling variable before it, even if you don't intend to use that functionality (just pass false in that case).

    You can also pass in an array of no watch variables to tie a number of them to the Chronicle object (note - this does NOT work with watch variables, you may only have one watch variable).

    $scope.watchThis = 'abc';
    $scope.dontWatchThis = 12;
    $scope.norThis = "I'm being";
    $scope.ignorePlease = {};
    $scope.chron = Chronicle.record('watchThis', $scope, false, ['dontWatchThis', 'norThis', 'ignorePlease']);
    ...
    $scope.dontWatchThis = 5;
    ...
    $scope.watchThis = 'cba';
    ...
    $scope.ignorePlease.member = 3;
    $scope.norThis += " ignored";
    ...
    $scope.watchThis = 'abcdefg';
    ...
    $scope.ignorePlease.member = 1000;
    //So currently
    //watchThis='abcdefg', dontWatchThis = 5,
    //norThis = "I'm being ignored", ignorePlease = {member: 1000}
    ...
    $scope.chron.undo();
    //So now
    //watchThis='cba', dontWatchThis = 5,
    //norThis = "I'm being", ignorePlease = {}
    ...
    $scope.chron.undo();
    //So now
    //watchThis='abc', dontWatchThis = 12,
    //norThis = "I'm being", ignorePlease = {}
    ...
    $scope.chron.redo();
    $scope.chron.redo();
    //So now
    //watchThis='abcdefg', dontWatchThis = 5,
    //norThis = "I'm being ignored", ignorePlease = {member: 3}
    

    On Event Handlers

    You may want to perform a certain function when something happens in your Chronicle object. In order to do that, you may do as follows:

    $scope.chron.addOnUndoFunction($scope.fn)
    

    This will make Chronicle call $scope.fn() whenever the $scope.chron object sucessfully undoes.

    $scope.chron.addOnRedoFunction($scope.fn)
    

    This will make Chronicle call $scope.fn() whenever the $scope.chron object sucessfully redoes.

    $scope.chron.addOnAdjustFunction($scope.fn)
    

    This will make Chronicle call $scope.fn() whenever the $scope.chron object sucessfully registers a change that is not an undo or redo - bascially any update to the model that isn't caused by a direct call to undo() or redo().

    If you wish to call a function with arguements, I suggest the following structure, as at the moment Chronicle does not support that directly:

    $scope.fn = function(passedVars){...} //The function that you wish to call
    $scope.intermediatefn = function() {
      var passingVars = ...//Get vars
      $scope.fn(passingVars);
    }
    ...
    $scope.chron.addOnAdjustFunction($scope.intermediatefn);
    

    And if at any point you wish to remove any of these onEvent functions, just use $scope.watchObj.removeOnEventFunction($scope.fn), where Event can be Undo, Redo, or Adjust. These are used throughout all of my other examples to show/hide the undo/redo buttons.

    By @Blitzen, @SamMaier