我有一个AngularJS应用程序,它使用工厂将JSON数据加载到对象中。请注意,以下示例是真实应用程序的非常精简版本。用户对象使用getter / setter方法来访问其属性,因为真实应用程序必须处理比简单属性赋值更多的逻辑。这意味着我不能简单地绑定到视图上的ngModel - 我必须使用自定义指令。当我想对对象进行编辑时,我在控制器中创建对象的副本,让用户在视图中对其进行更改,最后在控制器中保存或取消这些更改。但是,当我调用angular.copy()
时,这两个对象似乎仍然是链接的,因为在一个对象中更改数据也会更改另一个对象中的数据。这是为什么?
这是我的JSFiddle:http://jsfiddle.net/SqUu3/4/ 以下是我的观点:
<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.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];
}
};
})
有趣的部分是IN THEORY...
行(我的JSFiddle中的第30行)。这不是创建一个独立的对象。
答案 0 :(得分:1)
name
和id
属性在User
工厂中作为私有属性被错误限制,因此在视图中无法访问name
(我认为它已经破坏了)双向约束)。
将它们绑定到工厂对象(使用this
),它应该被解析。
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>
答案 1 :(得分:0)
查看angular.copy的源代码,如果未指定destination,则将其设置为source。
if (!destination) {
destination = source;
最后,它只是:
return destination;