Abhijeet
Abhijeet

Reputation: 1545

Redo - Undo functionality using angular js for large data

Currently i'm creating a table dynamically, in that multiple rows get added dynamically (Similar to Excel).Table can have millions of rows.

For redo/undo functionality i've used Angular-Chronicle. Now redo/undo working perfectly when rows count is upto 100. How to improve redo/undo performance when data is too large.

Here is working demo.

Note : Pagination is not suit for my case.I want to load data on scroll.

Please suggest any other suitable angular plugin or any other way to achieve redo/undo functionality with better performance.

Upvotes: 0

Views: 1325

Answers (1)

oodavid
oodavid

Reputation: 2326

To summarise, you can add state management with a Memento Factory.

All the code you need is below, but there's a little more background on my blog: AngularJS Memento Factory.

function MementoFactory(){
  return function() {
    var memento = this;
    // Private properties
    var subjects = arguments; // We can track multiple objects or arrays
    var stack = []; // Each call to "save" makes a copy of every subject on the stack
    var currentIndex = 0; // The "current" position on the stack stack
    // Begin by saving the current state
    save();
    // Public properties
    memento.timestamp = null; // The timestamp for the current stack
    // Public methods
    memento.save = save;
    memento.canUndo = canUndo;
    memento.undo = undo;
    memento.canRedo = canRedo;
    memento.redo = redo;

    function save() {
      var snapshot = {
        timestamp: Date.now(), // The save time
        subjects: [], // Contains each of the subjects
      };
      for (var a = 0, al = subjects.length; a < al; a++) {
        snapshot.subjects.push(angular.copy(subjects[a]));
      }
      if (stack[currentIndex] && angular.equals(stack[currentIndex].subjects, snapshot.subjects)) {
        return; // Do nothing if the new snapshot is the same as the current snapshot
      }
      if (canRedo()) {
        stack = stack.slice(0, currentIndex + 1); // Since we can "redo" we must overwrite that timeline (consider Back To The Future: Part II)
      }
      memento.timestamp = snapshot.timestamp; // Store the time
      stack.push(snapshot);
      currentIndex = stack.length - 1;
    };
    function canUndo() {
      return currentIndex > 0;
    };
    function undo() {
      if (canUndo()) {
        restoreSnapshot(-1);
      }
    };
    function canRedo() {
      return currentIndex < stack.length - 1;
    };
    function redo() {
      if (canRedo()) {
        restoreSnapshot(+1);
      }
    };
    function restoreSnapshot(indexDiff) {
      currentIndex += indexDiff;
      var snapshot = stack[currentIndex];
      memento.timestamp = snapshot.timestamp; // Update the timestamp
      for (var s = 0, sl = snapshot.subjects.length; s < sl; s++) {
        if (snapshot.subjects[s] !== subjects[s]) {
          angular.copy(snapshot.subjects[s], subjects[s]);
        }
      }
    };
  };
};

angular
  .module('app')
  .factory('Memento', MementoFactory);

Create a new Memento(...) object, passing the non-primitive variables you want to track

ctrl.user = { name: 'David King', location: 'England' };
ctrl.tags = [ 'AngularJS', 'Angular', 'Firebase' ];
// Create a new Memento object
var memento = new Memento(ctrl.user, ctrl.tags);
// Expose the undo and redo methods
ctrl.canUndo = memento.canUndo;
ctrl.redo    = memento.redo;
ctrl.canRedo = memento.canRedo;
ctrl.undo    = memento.undo;

Add undo and redo buttons to your View

<button
  type="button"
  ng-click="ctrl.undo()"
  ng-disabled="!ctrl.canUndo">Undo</button>
<button
  type="button"
  ng-click="ctrl.redo()"
  ng-disabled="!ctrl.canRedo">Redo</button>

Save your Memento object, when appropriate

<input
  type="text"
  ng-model="ctrl.user.name"
  ng-change="ctrl.save()">
<input
  type="text"
  ng-model="ctrl.user.location"
  ng-change="ctrl.save()">

... and that’s it!

Upvotes: 3

Related Questions