JavaScript AngularJS - implementing “undo” with angular.copy fails

Following issue: Let's say, we have an object like this:

$scope.Something = { 'a' : { object... }, 'b' : { another object... } }

This Something-object is also rendered in the view as follows:

<div ng-repeat="s in Something">{{ Something[s].someProperty }}</div>

The user wants to edit Something.a. For this, we show him a form. Before the form is shown, I save the current Something.a as a copy:

$scope.copyForUndo= angular.copy($scope.Something.a);

Now, if the user clicks "Cancel", it gets:

$scope.Something.a = angular.copy($scope.copyForUndo);

But since then, the association seems to disappear. No matter, what changes the user now makes to Something.a, the view doesn't get updated.

Why?

I know, what the equality of objects is (for example, that { object1: true } != {object1 : true} but still I cannot understand, why it doesn't work.

Answer:1

If you can make $scope.Something an array, then you can edit the copy and then update the array when the changes are saved. It still provides an undo, but in reverse of how you presented it.

fiddle here: http://jsfiddle.net/GYeSZ/1/

function MyCtrl($scope) {
    $scope.Something = [
        { name: "Aye", desc: new Date() },
        { name: "Bee", desc: new Date() },
        { name: "See", desc: new Date() }
    ];

    $scope.edit = function(idx) {
        $scope.copy = angular.copy($scope.Something[idx]);
        $scope.idx = idx;
    }

    $scope.save = function() {
        $scope.Something[$scope.idx] = angular.copy($scope.copy);
        $scope.cancel();
    }

    $scope.cancel = function() {
        $scope.copy = null;
        $scope.idx = -1;
    }
}

Update There is an alternate syntax for ng-repeat that can be used to enumerate dictionaries to get their key. Using this syntax, you can use the data structure you describe in the question

fiddle here: http://jsfiddle.net/GYeSZ/3/

function MyCtrl($scope) {

    $scope.edit = function(key) {
        $scope.copy = angular.copy($scope.Something[key]);
        $scope.key = key;
    }

    $scope.Something = {
        "a": { name: "Aye", desc: new Date() },
        "b": { name: "Bee", desc: new Date() },
        "c": { name: "See", desc: new Date() }
    };

    $scope.save = function() {
        $scope.Something[$scope.key] = angular.copy($scope.copy);
        $scope.cancel();
    }

    $scope.cancel = function() {
        $scope.copy = null;
        $scope.key = null;
    }
}

Html

<div ng-repeat="(key, value) in Something" ....>

Answer:2

If the copy source is ng-repeat iterative item, you should use

angular.copy($scope.copyForUndo, $scopy.sourceItem)

insteads of

$scope.sourceItem = angular.copy($scope.copyForUndo)

Otherwise, your data-binding dom is not tracking because the $$hashkey of the iterative item was erased by the misuse copy statement.

https://github.com/angular/angular.js/blob/g3_v1_2/src/Angular.js#L777

Answer:3

it seems a bit odd but if you can save the original array $scope.Something then on canceling you can rebind it.

 // saving original array to get the original copy of edited object
 var originalArrayCopy = angular.copy($scope.Something);
 ............
 // When user clicks cancel then simply filter the originalArray to get the original copy, here i am assuming there is a field in object which can uniquely identify it. 
// var originalObject = originalArrayCopy .filter(function(elm)
   {
       if(editedObject.Id == elm.Id)
              return elm;
   } );
 // once i get the original object , i can rebind it to the currentObject which is being edited.
Answer:4

Non destructive form editing: http://egghead.io/lessons/angularjs-angular-copy-for-deep-copy

Synopsis:

  • create a pointer to the object that the user clicked edit
  • create a copy of object that user edits and can decide to save|cancel
Answer:5

I need to capitalize the first letter of various html elements such as p, h2, h3, h4. This stack question has helped me, however, it only targets the p tag. How can I say to target more than one tag? ...

I need to capitalize the first letter of various html elements such as p, h2, h3, h4. This stack question has helped me, however, it only targets the p tag. How can I say to target more than one tag? ...

  1. capitalize first letter javascript
  2. capitalize first letter python
  3. capitalize first letter excel
  4. capitalize first letter of each word
  5. capitalize first letter java
  6. capitalize first letter css
  7. capitalize first letter c#
  8. capitalize first letter r
  9. capitalize first letter of each word javascript
  10. capitalize first letter google sheets
  11. capitalize first letter php
  12. capitalize first letter after colon
  13. capitalize first letter of each word python
  14. capitalize first letter ruby
  15. capitalize first letter sql
  16. capitalize first letter in quote
  17. capitalize first letter of each word java
  18. capitalize first letter word
  19. capitalize first letter of word javascript
  20. capitalize first letter javascript es6

HTML: <select id="mySelect"> <option>Apple</option> <option>Orange</option> <option>Pineapple</option> <option>Banana</option> &...

HTML: <select id="mySelect"> <option>Apple</option> <option>Orange</option> <option>Pineapple</option> <option>Banana</option> &...

  1. javascript item method

So let's say we have the following XML structure: <property> <label>Label 1</label> <value>value 1</label> </property> <property> <label>...

So let's say we have the following XML structure: <property> <label>Label 1</label> <value>value 1</label> </property> <property> <label>...

  1. can getelementsbytagname return null
  2. what does getelementsbytagname return
  3. what does document getelementsbytagname return

I had a simple script working using Promises in ECMA6 but I'm now converting what I wrote to jQuery to be compatible for all browsers but the issue I'm having is that availableDevices contains an ...

I had a simple script working using Promises in ECMA6 but I'm now converting what I wrote to jQuery to be compatible for all browsers but the issue I'm having is that availableDevices contains an ...

  1. jquery function after page load
  2. jquery function after delay
  3. jquery function after load
  4. jquery function after function complete
  5. jquery function after .each() has completed
  6. jquery function after function
  7. jquery function after animate
  8. jquery function after append
  9. jquery function after fadeout
  10. jquery function after another
  11. jquery function after timeout
  12. jquery function after time
  13. jquery function after ajax load
  14. jquery function after click event
  15. jquery function after page load complete
  16. jquery function after click
  17. jquery function after load html
  18. jquery function after dom load
  19. jquery function after submit
  20. jquery function after addclass