Skip to content Skip to sidebar Skip to footer

Angular.copy() Isn't Creating An Independent Copy Of An Object

I have an AngularJS app that uses a factory to load JSON data into objects. Note that the following example is a VERY stripped down version of the real-life app. The user objects

Solution 1:

The name and id properties were bounded incorrectly as private properties in the User factory, so name could not be accessed in the view (and I assume it broke the two-way binding).

Bind them to the factory object (using this) and it should be resolved.

angular.module('foo', [])
.controller('ctrl', function($scope, UserFactory)
{
    // Maps user IDs to user objects// Using hash instead of array for fast access by ID
    $scope.users = UserFactory.load();
    // Maps IDs of users to copies of the respective user objects, used for editing
    $scope.editUsers = {};
    
    // Return whether or not we're editing the user
    $scope.inEditMode = function(userID)
    {
        return $scope.editUsers.hasOwnProperty(userID);
    };
    
    // Copy the changes made to the actual user object
    $scope.saveChanges = function(userID)
    {
        $scope.users[userID] = angular.copy($scope.editUsers[userID]);
        // Don't need the edit-copy, so get rid of itdelete $scope.editUsers[userID];
    };

    // Turn edit mode on/off
    $scope.setEditMode = function(userID, inEditMode)
    {
        if(inEditMode)
        {
            // IN THEORY, this should create two independent copies of the same object
            $scope.editUsers[userID] = angular.copy($scope.users[userID]);
            
            /**
             * PROOF THESE ARE THE SAME OBJECTS:
             * This shouldn't affect the edit-copy in the view, but it does
             */
            $scope.users[userID].setName("WHY IS THIS THE SAME");
        }
        else
        {
            // We are effecively canceling the changes we've madedelete $scope.editUsers[userID];
        }
    };
})
.factory('UserFactory', function(User)
{
    return {
        load: function()
        {
            // Simulate a JSON response with user datavar rawUserData = [
                {id: 1, name: "Dave"},
                {id: 2, name: "Brian"}
            ];
            var userIDsToObjects = {};
            
            for(var userIter = 0;userIter < rawUserData.length;userIter++)
            {
                userIDsToObjects[rawUserData[userIter].id] = newUser(rawUserData[userIter].id, rawUserData[userIter].name);
            }
            
            return userIDsToObjects;
        }
    }
})
.factory('User', function()
{
    returnfunction(newID, newName)
    {
        this.getID = function()
        {
            returnthis.id;
        };
        
        this.getName = function()
        {
            returnthis.name;
        };
        
        this.setID = function(newID)
        {
            this.id = +newID;
        };
        
        this.setName = function(newName)
        {
            this.name = newName;
        };
        
        var self = this;
        // var id;// var name;
        
        (function()
         {
             self.setID(newID);
             self.setName(newName);
         })();
    }
})
.directive('ngModelGetter', function()
{
    return {
        require: "ngModel",
        //controller: "ctrl",link:  function(scope, element, attrs, ngModelCtrl)
        {
            var getExpression = attrs.ngModelGetter;
            
            functionupdateViewValue(newValue, oldValue)
            {
                if(newValue != ngModelCtrl.$viewValue)
                {
                    ngModelCtrl.$setViewValue(newValue);
                    ngModelCtrl.$render();
                }
                
                var updateExpression = attrs.ngModel + "=" + getExpression;
                scope.$eval(updateExpression);
            }
            
            updateViewValue();
            
            scope.$watch(getExpression, updateViewValue);
        }
    };
})
.directive('ngModelSetter', function()
{
    return {
        require: "ngModel",
        //controller: "ctrl",link:  function(scope, element, attrs, ngModelCtrl)
        {
            var setExpression = attrs.ngModelSetter;
            
            functionupdateModelValue(e)
            {
                scope.$value = ngModelCtrl.$viewValue;
                scope.$eval(setExpression);
                delete scope.$value;
            }
            
            scope.$watch(attrs.ngModel, updateModelValue);
        }
    };
})
<scriptsrc="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script><divng-app="foo"ng-controller="ctrl"><divng-repeat="(userID, user) in users"><spanng-if="inEditMode(userID)"><inputtype="text"ng-model="$name"ng-model-getter="editUsers[userID].getName()"ng-model-setter="editUsers[userID].setName($value)" /><buttonng-click="saveChanges(userID)">Save</button><buttonng-click="setEditMode(editUsers[userID].getID(), false)">Cancel</button></span><spanng-if="!inEditMode(userID)">
            {{user.getName()}}
            <buttonng-click="setEditMode(userID, true)">Edit</button></span></div></div>

Solution 2:

Looking at the source for angular.copy, if you do not specify destination, it is set to source.

if (!destination) {
    destination = source;

In the end, it just:

return destination;

Post a Comment for "Angular.copy() Isn't Creating An Independent Copy Of An Object"