angular.copy()不创建对象的独立副本 [英] angular.copy() isn't creating an independent copy of an object

查看:340
本文介绍了angular.copy()不创建对象的独立副本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用一个工厂JSON数据加载到一个对象应用AngularJS。注意,下面的例子是现实生活中的应用的一个非常精简版本。用户对象使用getter / setter方法​​来访问他们的属性,因为现实生活中的应用必须比简单的属性赋值处理更多的逻辑。这意味着我不能简单地绑定到ngModel的看法 - 我要使用自定义指令。当我想要编辑的对象,我创建了控制器对象的副本,让用户进行视图更改,并在最后保存或取消控制器这些变化。然而,当我打电话 angular.copy(),两个对象似乎仍然联系的,因为在一个不断变化的数据也是在其他变化的数据。这是为什么?

下面是我的jsfiddle: http://jsfiddle.net/SqUu3/4/
这是我的看法:

 < D​​IV NG-应用=foo的NG-控制器=CTRL>
    < D​​IV NG重复=(用户ID,用户)的用户>
        <跨度NG-IF =inEditMode(用户ID)>
            <输入类型=文本
            NG-模式=$名
            NG-模型拼命三郎=editUsers [用户ID] .getName()
            NG-模型二传手=editUsers [用户ID] .setName($值)/>
            <按钮NG点击=调用SaveChanges(用户ID)>保存< /按钮>
            <按钮NG点击=setEditMode(editUsers [用户ID] .getID(),FALSE)>取消< /按钮>
        < / SPAN>
        <跨度NG-IF =&GTinEditMode(用户ID)!;
            {{user.getName()}}
            <按钮NG点击=setEditMode(用户ID,真实)>编辑< /按钮>
        < / SPAN>
    < / DIV>
< / DIV>

下面是我的控制器:

  angular.module('富',[])
.controller('CTRL',函数($范围,UserFactory)
{
    //地图用户ID的用户对象
    //使用哈希值,而不是数组由ID快速访问
    $ scope.users = UserFactory.load();
    //用户到各个用户对象的副本的地图的ID,用于编辑
    $ scope.editUsers = {};    //返回我们是否正在编辑的用户
    $ scope.inEditMode =功能(用户ID)
    {
        返回$ scope.editUsers.hasOwnProperty(用户ID);
    };    //复制到实际用户对象所做的更改
    $ scope.saveChanges =功能(用户ID)
    {
        $ scope.users [用户ID] = angular.copy($ scope.editUsers [用户ID]);
        //不需要编辑,拷贝,所以摆脱它
        删除$ scope.editUsers [用户ID]
    };    //打开编辑模式的ON / OFF
    $ scope.setEditMode =功能(用户ID,inEditMode)
    {
        如果(inEditMode)
        {
            //从理论上讲,这应该创建同一对象的两个独立的副本
            $ scope.editUsers [用户ID] = angular.copy($ scope.users [用户ID]);            / **
             *证明它们是相同的对象:
             *这应该不会影响在视图编辑复制,但它确实
             *请注意,我只是在做这一次调用证明angular.copy()
             *不给我的用户对象的一个​​新的,独立的副本
             * /
            $ scope.users [用户ID] .setName(为什么这相同的);
        }
        其他
        {
            //我们effecively取消,我们所做的更改
            删除$ scope.editUsers [用户ID]
        }
    };
})

有趣的部分是(在我的jsfiddle第30行)的,理论... 行。这不是创建一个独立的对象。


解决方案

名称 ID 性质进行了界定错误地作为用户厂家私有属性,因此名称无法在视图中访问了(而我认为它打破了双向绑定)。

绑定他们到工厂对象(使用这个),它应该得到解决。

\r
\r

angular.module('富',[])\r
.controller('CTRL',函数($范围,UserFactory)\r
{\r
    //地图用户ID的用户对象\r
    //使用哈希值,而不是数组由ID快速访问\r
    $ scope.users = UserFactory.load();\r
    //用户到各个用户对象的副本的地图的ID,用于编辑\r
    $ scope.editUsers = {};\r
    \r
    //返回我们是否正在编辑的用户\r
    $ scope.inEditMode =功能(用户ID)\r
    {\r
        返回$ scope.editUsers.hasOwnProperty(用户ID);\r
    };\r
    \r
    //复制到实际用户对象所做的更改\r
    $ scope.saveChanges =功能(用户ID)\r
    {\r
        $ scope.users [用户ID] = angular.copy($ scope.editUsers [用户ID]);\r
        //不需要编辑,拷贝,所以摆脱它\r
        删除$ scope.editUsers [用户ID]\r
    };\r
\r
    //打开编辑模式的ON / OFF\r
    $ scope.setEditMode =功能(用户ID,inEditMode)\r
    {\r
        如果(inEditMode)\r
        {\r
            //从理论上讲,这应该创建同一对象的两个独立的副本\r
            $ scope.editUsers [用户ID] = angular.copy($ scope.users [用户ID]);\r
            \r
            / **\r
             *证明它们是相同的对象:\r
             *这应该不会影响在视图编辑复制,但它确实\r
             * /\r
            $ scope.users [用户ID] .setName(为什么这相同的);\r
        }\r
        其他\r
        {\r
            //我们effecively取消,我们所做的更改\r
            删除$ scope.editUsers [用户ID]\r
        }\r
    };\r
})\r
.factory('UserFactory',功能(用户)\r
{\r
    返回{\r
        载:函数()\r
        {\r
            //模拟用户数据的JSON响应\r
            VAR rawUserData = [\r
                {ID:1,名称:戴夫},\r
                {ID:2,名称:布赖恩}\r
            ];\r
            变种userIDsToObjects = {};\r
            \r
            为(VAR userIter = 0; userIter&下; rawUserData.length; userIter ++)\r
            {\r
                userIDsToObjects [rawUserData [userIter] .ID =新用户(rawUserData [userIter] .ID,rawUserData [userIter]。名称);\r
            }\r
            \r
            返回userIDsToObjects;\r
        }\r
    }\r
})\r
.factory(用户,函数()\r
{\r
    返回功能(NEWID,了newName)\r
    {\r
        this.getID =功能()\r
        {\r
            返回this.id;\r
        };\r
        \r
        this.getName =功能()\r
        {\r
            返回this.name;\r
        };\r
        \r
        this.setID =功能(NEWID)\r
        {\r
            this.id = + NEWID;\r
        };\r
        \r
        this.setName =功能(了newName)\r
        {\r
            this.name =了newName;\r
        };\r
        \r
        VAR自我=这一点;\r
        // VAR ID;\r
        //变量名称;\r
        \r
        (功能()\r
         {\r
             self.setID(NEWID);\r
             self.setName(了newName);\r
         })();\r
    }\r
})\r
.directive('ngModelGetter',函数()\r
{\r
    返回{\r
        要求:ngModel\r
        //控制器:CTRL\r
        链接:功能(范围,元素,ATTRS,ngModelCtrl)\r
        {\r
            VAR GETEX pression = attrs.ngModelGetter;\r
            \r
            功能updateViewValue(为newValue,属性oldValue)\r
            {\r
                如果(为newValue!= ngModelCtrl。$ viewValue)\r
                {\r
                    ngModelCtrl $ setViewValue(newValue)以。\r
                    。ngModelCtrl $渲染();\r
                }\r
                \r
                VAR updateEx pression = attrs.ngModel +=+ GETEX pression;\r
                。范围$的eval(updateEx pression);\r
            }\r
            \r
            updateViewValue();\r
            \r
            。范围$腕表(GETEX pression,updateViewValue);\r
        }\r
    };\r
})\r
.directive('ngModelSetter',函数()\r
{\r
    返回{\r
        要求:ngModel\r
        //控制器:CTRL\r
        链接:功能(范围,元素,ATTRS,ngModelCtrl)\r
        {\r
            VAR SETEX pression = attrs.ngModelSetter;\r
            \r
            功能updateModelValue(五)\r
            {\r
                范围$值= ngModelCtrl $ viewValue。\r
                范围$的eval(SETEX pression)。\r
                删除范围$价值。\r
            }\r
            \r
            。范围$腕表(attrs.ngModel,updateModelValue);\r
        }\r
    };\r
})

\r

&LT;脚本SRC =htt​​ps://ajax.googleapis.com/ajax /libs/angularjs/1.2.19/angular.min.js\"></script>\r
&LT; D​​IV NG-应用=foo的NG-控制器=CTRL&GT;\r
    &LT; D​​IV NG重复=(用户ID,用户)的用户&GT;\r
        &LT;跨度NG-IF =inEditMode(用户ID)&GT;\r
            &LT;输入类型=文本\r
            NG-模式=$名\r
            NG-模型拼命三郎=editUsers [用户ID] .getName()\r
            NG-模型二传手=editUsers [用户ID] .setName($值)/&GT;\r
            &LT;按钮NG点击=调用SaveChanges(用户ID)&GT;保存&LT; /按钮&GT;\r
            &LT;按钮NG点击=setEditMode(editUsers [用户ID] .getID(),FALSE)&GT;取消&LT; /按钮&GT;\r
        &LT; / SPAN&GT;\r
        &LT;跨度NG-IF =&GTinEditMode(用户ID)!;\r
            {{user.getName()}}\r
            &LT;按钮NG点击=setEditMode(用户ID,真实)&gt;编辑&LT; /按钮&GT;\r
        &LT; / SPAN&GT;\r
    &LT; / DIV&GT;\r
&LT; / DIV&GT;

\r

\r
\r

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 use getter/setter methods to access their properties because the real-life app must handle more logic than a simple property assignment. This means I cannot simply bind to the ngModel on the view - I have to use custom directives. When I want to make edits to an object, I create a copy of the object in the controller, let users make changes to it in the view, and at the end either save or cancel those changes in the controller. However, when I'm calling angular.copy(), the two objects still seem linked because changing data in one also changes data in the other. Why is this?

Here is my JSFiddle: http://jsfiddle.net/SqUu3/4/ Here is my view:

<div ng-app="foo" ng-controller="ctrl">
    <div ng-repeat="(userID, user) in users">
        <span ng-if="inEditMode(userID)">
            <input type="text"
            ng-model="$name"
            ng-model-getter="editUsers[userID].getName()"
            ng-model-setter="editUsers[userID].setName($value)" />
            <button ng-click="saveChanges(userID)">Save</button>
            <button ng-click="setEditMode(editUsers[userID].getID(), false)">Cancel</button>
        </span>
        <span ng-if="!inEditMode(userID)">
            {{user.getName()}}
            <button ng-click="setEditMode(userID, true)">Edit</button>
        </span>
    </div>
</div>

Here is my controller:

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 it
        delete $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
             * Note that I am only doing this next call to prove that angular.copy() 
             * isn't giving me a new, independent copy of the user object
             */
            $scope.users[userID].setName("WHY IS THIS THE SAME");
        }
        else
        {
            // We are effecively canceling the changes we've made
            delete $scope.editUsers[userID];
        }
    };
})

The interesting part is the IN THEORY... line (line 30 in my JSFiddle). This isn't creating an independent object.

解决方案

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 it
        delete $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 made
            delete $scope.editUsers[userID];
        }
    };
})
.factory('UserFactory', function(User)
{
    return {
        load: function()
        {
            // Simulate a JSON response with user data
            var rawUserData = [
                {id: 1, name: "Dave"},
                {id: 2, name: "Brian"}
            ];
            var userIDsToObjects = {};
            
            for(var userIter = 0;userIter < rawUserData.length;userIter++)
            {
                userIDsToObjects[rawUserData[userIter].id] = new User(rawUserData[userIter].id, rawUserData[userIter].name);
            }
            
            return userIDsToObjects;
        }
    }
})
.factory('User', function()
{
    return function(newID, newName)
    {
        this.getID = function()
        {
            return this.id;
        };
        
        this.getName = function()
        {
            return this.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;
            
            function updateViewValue(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;
            
            function updateModelValue(e)
            {
                scope.$value = ngModelCtrl.$viewValue;
                scope.$eval(setExpression);
                delete scope.$value;
            }
            
            scope.$watch(attrs.ngModel, updateModelValue);
        }
    };
})

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.19/angular.min.js"></script>
<div ng-app="foo" ng-controller="ctrl">
    <div ng-repeat="(userID, user) in users">
        <span ng-if="inEditMode(userID)">
            <input type="text"
            ng-model="$name"
            ng-model-getter="editUsers[userID].getName()"
            ng-model-setter="editUsers[userID].setName($value)" />
            <button ng-click="saveChanges(userID)">Save</button>
            <button ng-click="setEditMode(editUsers[userID].getID(), false)">Cancel</button>
        </span>
        <span ng-if="!inEditMode(userID)">
            {{user.getName()}}
            <button ng-click="setEditMode(userID, true)">Edit</button>
        </span>
    </div>
</div>

这篇关于angular.copy()不创建对象的独立副本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆