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

时间:2013-08-12 12:19:22

标签: javascript angularjs

使用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”放入过滤器输入:显示列表更新, 监听器被称为两次(首先使用前一个过滤器的数据,然后使用当前数据),显示的长度是正确的。
  3. 这是小提琴。请查看控制台日志,了解实际调用侦听器的时间。

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

    有趣的观察:如果我在页面的某处使用{{filteredItems}},则会正确调用侦听器。

    我对AngularJS有什么不了解的吗?有什么想法吗?

2 个答案:

答案 0 :(得分:2)

我不太确定为什么它正在这样做但似乎有角度正在推迟它的摘要周期。除非你想调试角度本身以找出原因,否则我建议两种方法。

在大多数情况下,如果您不想更改示波器本身的某些内容(即观察更改并调用其他功能,而不是修改示波器),则会使用手动监视。你想要实现的是与计算的observable的概念密切相关,但同时,你只返回另一个对象的属性而不转换它。

直接使用该属性

在模板中,您可以直接使用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>

修改:这是第二个选项的updated fiddle

答案 1 :(得分:1)

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