我在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。 :)
答案 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));
}