Angular JS implementation of the undo redo feature allows users to reverse or

My challenge involves a large object stored in $rootScope with over 100 objects, each containing hierarchies of objects and arrays. I am seeking to watch the entire $rootScope using deepWatching (setting the 3rd parameter of $watch to TRUE).

The issue arises when $watch returns two objects - the Old RootScope and the Modified RootScope. This requires me to analyze which attribute of the object changed within $rootScope and its hierarchy in order to push it into a stack.

Is there a straightforward method to identify the exact attribute that has changed while watching a $scope?

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

   //I need to pinpoint the specific attribute that changed, not just the entire objects!

}, true);

Alternatively, I could individually watch each attribute of the object but this approach seems overly resource-intensive.

What is the most effective way to implement undo/redo functionality in AngularJS?

Note:

  1. The angular-history solution does not meet my requirements as I need to monitor all attributes of an object, some of which may contain other objects and arrays.

  2. I understand that watching the entire $rootScope may not be optimal, however, I am developing a UI with multiple grids, drag-and-drop features, possibly forms, and elements that can be deleted. Therefore, I aim to create a comprehensive solution for tracking changes and enabling undo functionality with CTRL + Z. Think of replicating the desktop version of Photoshop.

Answer №1

Revoking and reapplying actions is centered around the concept of the command pattern. Brace yourself for a plethora of watch statements; such robust functionality doesn't come without its cost.

Just for kicks, let's explore a straightforward (yet highly practical and scalable) implementation: http://jsfiddle.net/aBcDe/2/

A command acts as an interface for objects capable of executing and reverting changes:

function YyyCommand() {
    // specific details go here
}
Command.prototype.execute = function() {
    // specific details go here
};
Command.prototype.rollback = function() {
    // specific details go here
};

To use this system, you apply a directive to your <input> elements:

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

Then, enclose these elements within a container (like the <form>) featuring the undo-support directive:

<form undo-support>

The link function in undoable monitors changes in the <input> and enlists a command with the undoableSupport. The controller of undoableSupport maintains a record of all commands.

Answer №2

If you're looking for an undo/redo solution, consider checking out the Angular-Chronicle library on GitHub. Personally, I found it to be a more favorable choice compared to angular-history.

Answer №4

Instead of constantly monitoring the entire $rootScope or any other object containing numerous nested items, each requiring iteration during every digest cycle, consider utilizing an array as a queue to track changes made to the original data model. In addition, maintain a variable indicating the current position in this queue.

By encapsulating this array and variable within a service, you can easily update the index when performing undo/redo actions. This allows for seamless monitoring of the variable from various components, such as directives, that need to react to these modifications.

Answer №5

If you're searching for a solution, LazyJsonUndoRedo might be exactly what you need.

Check it out here: https://github.com/azazdeaz/LazyJsonUndoRedo

This tool offers a seamless history handler with automatic undo/redo capabilities for nested javascript objects. It utilizes ES6 Object.observe() or Polymer shim.

Learn more about LazyJsonUndoRedo at

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Some sections of the HTML form are failing to load

I'm currently following a tutorial and applying the concepts to a Rails project I had previously started. Here's my main.js: 'use strict'; angular.module('outpostApp').config(function ($stateProvider) { $stateProvider.sta ...

Ways to utilize the Math.max and Math.min functions within an Array

Attempting to use this approach on an array to retrieve both the minimum and maximum values proved unsuccessful. var array = [3, 6, 1, 5, 0, -2, 3]; var min = Math.min( array ); var max = Math.max( array ); document.write(max); ...

Performing a mouse hover action with Selenium WebDriver

Can someone guide me on how to perform a mouse hover action in Selenium WebDriver? The mouse hover action needs to be done on a tab where it hovers and then clicks on the tab. Is there a way to achieve this using JavaScript executor and java? ...

Improperly nested *ngFor causing index errors

My *ngFor loop is returning incorrect indexes, specifically for the last objects in the array. For example, when iterating over "option 2" and "option c," the indexes are switched (1 and 0 instead of 0 and 1). The issue only affects the last objects in eac ...

The tooltip function is not functioning properly on Internet Explorer when the button is disabled

I have been utilizing a similar solution found at http://jsfiddle.net/cSSUA/209/ to add tooltips to disabled buttons. However, I am encountering an issue specifically in Internet Explorer (IE11). The workaround involves wrapping the button within a span: ...

learning how to combine two json arrays of objects and showcase them in a react component

What is the best way to combine this data and present it in a table with a map using React? The text will be in the first column and the count in the second. const handleSubmit = async (event) => { event.preventDefault(); let URL1 = " ...

Issues arise with tabbed content as default content fails to display and classes are not added upon clicking

I'm currently working on a tabbed module that consists of three tabs. Here's how it operates: When the user clicks on a carousel_element, it reveals carousel_hidden-text within that div and displays it in another div called carousel_quote. I&ap ...

Having trouble getting the Babel plugin transform-remove-console to function properly with the Vue CLI 4 and @vue/cli-plugin-babel/preset?

When you create a VueJS project using Vue CLI 4, Babel is configured with a useful preset in babel.config.js: module.exports = { presets: [ '@vue/cli-plugin-babel/preset', ], }; I am attempting to use babel-plugin-transform-remove-conso ...

The collapsible section expands upon loading the page

Trying to implement Disqus as a plugin on my test webpage, I created a collapsible section with a "Show comments" button. The idea was to hide the plugin by default and reveal it only when users click on "Show comments". Despite using the w3schools example ...

Assistance with themed implementation for single-page web applications in JavaScript

My goal is to incorporate theme support into my single page application. The catch is that the theme change needs to be done locally through JavaScript without making any server calls, in order to work in offline mode. Since I am using angularjs, the HTML ...

The invokeApply parameter within $interval remains unchanged and has no effect

In the documentation for AngularJS's $interval service, it is mentioned: invokeApply (optional) boolean: If set to false, skips model dirty checking. Otherwise, will invoke the function within the $apply block. This implies that setting invokeApp ...

How to properly fill state values for testing components with React Testing Library?

Introducing my custom component -> export default() => { const [list, setList] = useState([]) const handleAddToList = () => { // Executes an API call and updates the list state. setList(response); } return ( <div> ...

Placing the cursor on a newly created element is ineffective when the innerHtml is empty

https://codesandbox.io/s/nameless-feather-duk25?fontsize=14&hidenavigation=1&theme=dark In the code example above, I am trying to make a certain feature work. The concept is as follows: there is a contentEditable div element where text can be type ...

The ideal setup for ngResource - achieving the perfect configuration

In my application, I have three main objects: Games, Questions, and Answers. Here is how the classes are configured: class Game{ id; Question[] questions; } class Question{ id; text; Answer[] answers; } class Answer{ id; text; ...

Distinguishing AngularJs dependency Injection

Do I need the array in the code below? app.controller('myController', ['$scope', function($scope){ }]) Will the second code snippet function the same as the first one? app.controller('myController', function($scope ...

Interactive hover image that reveals hidden information when clicked

I am trying to implement 3 rollover images as triggers to display text boxes underneath each image. However, when I click on the image, it disappears and does not function properly. I am currently focusing on getting the first one to work. To view my code ...

The image you are looking for at './expo-asset/assets/${your_img_path}' was not located within React Native or Expo

My current project is an expo ejected react native project and I'm encountering the error message [native] Could not find image on path The file path in question: 'file:///var/mobile/Containers/Data/Application/..../Library/Application%20Support ...

I am experiencing an issue where the custom form validation directive in AngularJS is not functioning properly within my modal

To enhance the validation of my input boxes, I created a custom directive called nx-validate. This directive is designed to be added to a bootstrap div.form-group, which will then check if the <input/> field is $dirty or $invalid, and apply the appro ...

Trim JSON using JavaScript

I'm currently working on reducing a JSON array. Within the array, there are other objects, and my goal is to convert their attributes into their own separate array. Reduce Function: // parsed.freight.items is the path var resultsReduce = parsed.frei ...

Incorporating a for loop, ExpressJS and Mongoose repeatedly utilize the create method to generate

When users input tags separated by commas on my website, ExpressJS is supposed to search for those tags and create objects if they don't already exist. Currently, I am using a for loop to iterate through an array of tags, but only one object is being ...