防止 $scope 变量副本监听其原始内容

Prevent $scope variable copy from listening to its original

在我的 AngularJS 应用程序中,我通常使用 $rootScope 传递数据。在各种状态下,我会在必要时操纵将它们拉入 $scope 。但由于 $scope 具有原型继承,我想这只是为了可读性而不是其他。在我的应用程序中,有以下变量...

$rootScope.list // object
$rootScope.list.items // array
$rootScope.additions // array
$rootScope.cart // object
$rootScope.cart.items // array

在我开始之前,这通常是用伪代码列出的。所以如果它不正确,没关系。我正在寻找概念上的理解。

起初,购物车是空的。您查看 list.items 并将单个列表项复制到 cart.items 数组中。这很好用,直到我们开始讨论具有唯一性 additions.

的重复项目

说,我想要 2 个相同的项目,除了一个项目有不同的添加(购物车项目有数量 属性,而不是列表项目)。

$rootScope.cart.items = [
   {
      name: 'listItem1',
      quantity: 3,
      additions: ['addition1']
   }, {
      name: 'listItem2',
      quantity: 1,
      additions: ['addition2']
   }, {
      name: 'listItem1', // same as cart.items[1], but additions has diff values
      quantity: 1,
      additions: ['addition1','addition2']
   }];

首先,AngularJS 抱怨说我的 ng-repeater 中有重复项。我通过在 ng-repeat 指令中附加 track by $index 解决了这个问题。这也是我看到的第一个红旗,告诉我我做错了什么。

我的第一个想法是创建一个临时 cartItem,任意添加它的添加项,然后将其推送到 $rootScope.cart.items[]。但是,当将第 3 个购物车项目推入购物车时,它是第一个列表项目的另一个副本,它会覆盖第一个购物车项目的 additions[]。所以,$rootScope.cart.items 看起来像这样:

$rootScope.cart.items = [
   {
      name: 'listItem1',
      quantity: 3,
      additions: ['addition1','addition2']
   }, {
      name: 'listItem2',
      quantity: 1,
      additions: ['addition2']
   }, {
      name: 'listItem1', // same as cart.items[1], but additions has diff values
      quantity: 1,
      additions: ['addition1','addition2']
   }];

从这个结果来看,在将 $rootScope 变量从一个地方复制到另一个地方时,它表面上是按引用传递的。

因此,我的下一个想法是尝试使用具有隔离作用域的指令。但我很快就意识到我的逻辑是错误的。虽然隔离范围会给我单向数据绑定,但它并不是我希望的方向。该指令的父级范围仍然可以设置值。

我最理想的做法是将 list.items 任意添加到 cart.items。但是来自同一列表项的任何购物车项目,我都希望彼此解除绑定。因此,一个副本中的添加可以不同于另一个副本中的添加。 实现此目标的最佳方法是什么?我觉得在我的应用程序中使用 $rootScope 在状态之间传递数据可能是一个糟糕的决定,尽管它是我找到的最快方法。

当您编辑第一个购物车商品的 additions 时,您实际上编辑了第一个和第三个购物车商品的 additions,因为它们是相同的。

当您创建第三个购物车商品时,您可以复制第一个商品的属性:

var newCartItem = {};

newCartItem.name = firstCartitem.name;
newCartItem.quantity = firstCartitem.quantity;
// Do not point to the other array, instead create a new one and copy over
newCartItem.additions = [];
for (var i = 0; i < firstCartitem.additions.length; i++) {
  newCartItem.additions.push( firstCartitem.additions[i] );
}
// Now you can add anything to the new cart item without editing the first one    

Angular文案就是你要找的。

https://docs.angularjs.org/api/ng/function/angular.copy

您将在文档中看到一些使用方法,但我和我的同事发现使用可选的 'destination' 选项更好。

这就是它的工作原理

首先设置你的新目标阵列。

var newArray = [];

然后将其用作 angular 复制中的目标

angular.copy(firstArray, newArray);

好了,就是这么简单!

现在您的新阵列将不再连接到原始阵列。

我还会考虑使用工厂作为代码的服务,而不是使用 rootScope。学习这将大大提高您的工作效率,并使您的代码更具可读性和用户友好性。这是一篇关于它的好文章。

http://tylermcginnis.com/angularjs-factory-vs-service-vs-provider/

编辑

而且正如评论中有人提到的那样,它确实是深拷贝。这意味着它将复制数组内的所有子对象和数组。