1

在使用 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”放入过滤器输入:显示的列表得到更新,没有调用监听器,所以显示的长度是错误的。
  2. 将“aaa”放入过滤器输入:显示的列表被更新,监听器被调用两次(首先使用来自前一个过滤器的数据,然后使用当前数据),显示的长度是正确的。

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

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

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

我对 AngularJS 有什么不明白的地方吗?有任何想法吗?

4

2 回答 2

2

我不太确定它为什么这样做,但似乎 angular 正在推迟其消化周期。除非您想自行调试 Angular 以找出原因,否则我建议您使用两种替代方法。

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

直接使用该属性

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

<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>

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

于 2013-08-12T12:48:07.760 回答
1

我认为这是 jsfiddle 使用的 Angular 1.1.1 中的一个错误。如果您更新到 1.1.5,它会按预期工作,并且每次过滤匹配的数量发生变化时都会运行 $watch - http://jsfiddle.net/7QJJK/10/

于 2013-08-12T12:55:19.320 回答