Angular $ watch with array splice

时间:2017-06-30 15:32:17

标签: javascript angularjs arrays watch splice

我在Angular和数组拼接方面遇到了关于观察者的问题。结合他们两个都有一个非常奇怪的行为。所以我创建了一个小型演示,其中包含显示正在发生的问题的日志。这是我正在使用的代码,或者您可以在此Plunker中看到它。

HTML:

<div class="logs">
    <div><b>LOGS:</b></div>
    <ul ng-repeat="watchEntry in watchersLogs track by $index">
      <li>{{watchEntry}}</li>
    </ul>
    <div><b>Watcher Count:</b> {{watchers.length}}</div>
</div>
<div class="item" ng-repeat="item in list" ng-init="InitItem()">
  <div class="item-title-wrapper">
    <span class="item-title">Item {{ $index + 1 }}</span>
    <button ng-click="AddNewItem()">Add New</button>
    <button ng-click="RemoveItem()">Remove</button>
  </div>
  <div class="field">
    <div>
      CREDIT:
      <input type="number" name="credit" ng-model="item.credit" />
    </div>
    <div>
      DEBIT:
      <input type="number" name="debit" ng-model="item.debit" />
    </div>
  </div>
</div>

JS:

var app = angular.module("listApp", []);
app.controller("listController", function($scope) {
  // list with all data:
  $scope.list = [{
    credit: 2000,
    debit: 0
  }, {
    credit: 100000,
    debit: 1000
  }];

  // list containing all watchers:
  $scope.watchers = [];
  // logs containing all watcher event:
  $scope.watchersLogs = [];

  function SetWatcher(itemIndex) {
    $scope.watchers.splice(itemIndex, 0, $scope.$watch("list[" + itemIndex + "]", function(newValues, oldValues, scope) {
      $scope.watchersLogs.push("Item " + itemIndex + " watcher fired!");
    }, true));
  }

  $scope.InitItem = function() {
    // set a watcher for newly create item:
    SetWatcher(this.$index);
  }

  $scope.AddNewItem = function() {
    var newItem = {
      credit: 0,
      debit: 0
    };

    // put the item into the array:
    $scope.list.splice((this.$index + 1), 0, newItem);
  };

  $scope.RemoveItem = function() {
    // destroy the watcher:
    $scope.watchers[this.$index]();
    $scope.watchers.splice(this.$index, 1);
    // remove the item from the list:
    $scope.list.splice(this.$index, 1);
  };
});

的CSS:

.logs {
  margin-bottom: 50px;
}

.item {
  margin-bottom: 25px;
}

所以,正如你在那里看到的那样,当我在$scope.list数组中初始化或添加新项时,我正在为它分配一个新的观察者。从日志中你可以看到每个观察者只在启动时被解雇一次,这很好。但是,我的应用程序需要在您选择或正在查看的项目之后添加项目/e.g。如果你有3个项目,然后点击第二个的添加新按钮,它应该在第三个地方/上添加一个新条目。这就是我使用splice的原因。

但是,这有时导致$watch表达式多次执行。因此,如果您在最后一个位置添加新条目,它将仅针对它激发$watch表达式一次。没关系。但是,如果你把它添加到中间的某个地方/。第2个,第3个等等,观察者表达式不仅会被激发,而且还会被其后的所有其他项目激活。我猜测splice以某种方式更改了新创建的项目之后的项目引用,这就是$watch在发生这种情况时执行的原因。

我需要$watch表达式,因为我在更改值时正在进行一些计算,所以我想我无法摆脱它们。我也需要splice功能,正如我前面提到的那样......那么,任何想法如何解决这个问题?

提前致谢! :)

... UPDATE ...

我解决了这个问题!非常感谢@Deblaton Jean-Philippe的帮助。所以,首先我创建了一个删除观察者的新功能:

function RemoveWatcher(itemIndex) {
  if ($scope.watchers[itemIndex]) {
    // destroy the watcher:
    $scope.watchers[itemIndex]();
    // remove it from the array as well:
    $scope.watchers.splice(itemIndex, 1);
  }
}

我在设置新观察者之前调用它以确保它已被清除。我也在RemoveItem方法中调用它,但是这里的棘手问题是我总是从数组中删除最后一个观察者条目,因为我正在拼接$scope.list数组并且它更改字段的顺序。所以我在那里做的只是像这样调用RemoveWatcher方法:

RemoveWatcher($scope.watchers.length - 1);

这似乎完美无缺!您也可以查看更新的Plunker。 :)

1 个答案:

答案 0 :(得分:1)

代码正在做你要求他做的事情。他正在关注你所询问的指数的价值。

如果你添加了一个新的手表并且索引已经注册,它将被触发两次。

如果查看文档here,您会看到 $watch()返回一个函数。执行此功能将取消注册您的手表。

您需要做的是,当您添加新项目时,取消注册那里的手表。

也许这样的事可以帮到你:

  function SetWatcher(itemIndex) {
    if($scope.watchers[itemIndex]) $scope.watchers[itemIndex]();
    $scope.watchers.splice(itemIndex, 0, $scope.$watch("list[" + itemIndex + "]", function(newValues, oldValues, scope) {
      $scope.watchersLogs.push("Item " + itemIndex + " watcher fired!");
    }, true));
  }