过滤集合上的Angular watch侦听器在错误的时间调用

Angular watch listener on filtered collection called at the wrong time

本文关键字:错误 时间 调用 侦听器 watch 集合 Angular 过滤      更新时间:2023-09-26

我有一个有趣的问题,那就是使用AngularJS监视对已过滤集合的更改。

我的作用域中有一组项目,这些项目使用其属性之一进行筛选,并使用ng-repeat:进行渲染

    <input ng-model="searchName" placeholder="search"/>
    <ul>
        <li ng-repeat="i in (filteredItems = (items | filter:searchName))">
            {{i.name}}
        </li>
    </ul>

我想观察对filteredItems的更改,以便更新依赖于当前可见项的范围变量。对于这个例子,假设我想更新一个保持当前filteredItems长度的变量,并将其显示给用户:

<p>filtered items length: {{filteredItemsLen}}</p>

我在我的控制器中注册了filteredItems的侦听器:

$scope.$watch('filteredItems', function(newItems, oldItems) {
    $scope.filteredItemsLen = newItems.length;
}, true);

然而,在对filteredItems进行更改时,并不总是调用此侦听器。在下面的小提琴里,请试试这个:

  1. 在过滤器输入中输入"aa":显示的列表得到更新,listener未调用,因此显示的长度错误
  2. 在过滤器输入中输入"aaa":显示的列表得到更新,listener被调用两次(首先使用来自上一个过滤器的数据,然后使用当前数据),显示的长度是正确的

这是小提琴。请查看控制台日志,了解实际调用侦听器的时间。

http://jsfiddle.net/7QJJK/8/

有趣的观察:如果我在页面上的某个地方使用{{filteredItems}},那么侦听器就会被正确调用。

AngularJS有什么我不理解的地方吗?有什么想法吗?

我不太确定为什么要这样做,但它似乎在推迟其消化周期。除非你想调试angular本身以找出原因,否则我建议两种选择。

在大多数情况下,当您不想更改作用域本身的内容时,会使用手动监视(即监视更改并调用另一个函数,而不是修改作用域)。你想要实现的是与计算可观测的概念密切相关的,但同时,你只从另一个对象返回一个属性,而不转换它

直接使用属性

在模板中,可以直接使用filteredItems.length。因为它是一个数字,所以即使底层数组发生变化,angular也会在摘要周期中实现稳定。

<p>filtered items length: {{filteredItems.length}}</p>

在作用域上使用计算属性

由于filteredItems会在某个时刻出现在作用域上,所以这太过分了(除非您计算长度并返回不同的内容)。在范围中,您可以定义一个返回值的函数,angular将很高兴地观察其摘要周期中的变化:

function SomeCtrl($scope) {  
    $scope.items = [{name: 'aa'}, {name: 'ab'}, {name: 'ac'}];
    $scope.filteredItemsLen = function() {
        return ($scope.filteredItems || []).length;  
    };
    $scope.$watch('filteredItemsLen()', function(newValue, oldValue) {
        console.log('Filtered length changed from ' +
                    oldValue + ' to ' + newValue);
    });
}
<p>filtered items length: {{filteredItemsLen()}}</p>

编辑:这是第二个选项的更新小提琴

我认为这是jsfiddle使用的Angular 1.1.1中的一个错误。如果你更新到1.1.5,它会像你预期的那样工作,每次过滤匹配的数量改变时都会运行$watchhttp://jsfiddle.net/7QJJK/10/