How do I implement a filter in AngularJS?

时间:2016-04-15 15:14:37

标签: angularjs

How do I filter an array by a property that exists in another array?

I have this two arrays in my control:

this.items = [{ "Id": 1, "TypeId": 1, "name": "Michael" },
              { "Id": 2, "TypeId": 2, "name": "Helga" },
              { "Id": 3, "TypeId": 1, "name": "Max" },
              { "Id": 4, "TypeId": 2, "name": "Ann" }];

this.filterBy = [{ "Id": 1, "gender": "Male" },
                 { "Id": 2, "gender": "female" }];

And here is my ng-repeat in view template:

<tr ng-repeat="item in list.items">
    <td>
        <div class="container-fluid">
            <div class="row">
                <div class="text-center">{{ item.name }}</div>
            </div>
        </div>
    </td>
</tr>

Here is the generated view:

Enter image description here

At some point I want to filter items array by gender to show in the view above only females or only males.

I tried to implement filter, like this:

(function () {

    "use strict";

    angular.module("sensorsData").filter('filterByGender', filterByGender);

    function filterByGender() {
        var result = [];
        return function (items, filterBy) {
            if (!items)
                return;
            if (!filterBy)
                return;

            angular.forEach(items, function (item) {
                angular.forEach(filterBy, function (f) {
                    if (item.TypeId == f.Id) {
                        result.push(item);
                    }
                })
            });
            return result;
        }
    };
})();

And here how I use it in view template:

<tr ng-repeat="item in list.items | filterByGender:list.filterBy">
    <td>
        <div class="container-fluid">
            <div class="row">
                <div class="text-center">{{ item.name }}</div>
            </div>
        </div>
    </td>
</tr>

The desired view in case if content of filterBy array like that:

this.filterBy = [{ "Id": 1, "gender": "Male" }];

The view Should display only items rows with TypeId = 1.

Like this:

Enter image description here

But the filter above is not working.

And in the debugger I get this errors:

Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: r in list.arr | filterByAlert:list.filterBy, Duplicate key: object:8, Duplicate value: {"Id":1,"TypeId":1,"name":"Michael"}
http://errors.angularjs.org/1.5.3/ngRepeat/dupes?p0=r%20in%20list.arr%20%7C…%3A8&p2=%7B%22Id%22%3A1%2C%22TypeId%22%3A1%2C%22name%22%3A%22Michael%22%7D
    at angular.js:68
    at ngRepeatAction (angular.js:28799)
    at $watchCollectionAction (angular.js:16734)
    at Scope.$digest (angular.js:16869)
    at Scope.$apply (angular.js:17133)
    at done (angular.js:11454)
    at completeRequest (angular.js:11652)
    at XMLHttpRequest.requestLoaded (angular.js:11593)

Why is the filter not working? And how can I create a filter to show items by gender?

And this:

angular.js:13424 Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
http://errors.angularjs.org/1.5.3/$rootScope/infdig?p0=10&p1=%5B%5D
    at angular.js:68
    at Scope.$digest (angular.js:16907)
    at Scope.$apply (angular.js:17133)
    at done (angular.js:11454)
    at completeRequest (angular.js:11652)
    at XMLHttpRequest.requestLoaded (angular.js:11593)(anonymous function) @ angular.js:13424
angular.js:17136 Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []
http://errors.angularjs.org/1.5.3/$rootScope/infdig?p0=10&p1=%5B%5D

And this:

    Uncaught Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
    Watchers fired in the last 5 iterations: []

Why is the filter not working? And how can I create a filter to show items by gender?

2 个答案:

答案 0 :(得分:1)

您可以使用一些非常复杂的过滤功能解决方案,但在我看来,最简单的方法是创建一个合并的项目列表,其中包含您的视图所需的内容(即视图模型)。这样,您只需使用filter上的ng-repeat来过滤性别。

这是一个代码段:

&#13;
&#13;
var app = angular.module('myapp', []);
app.controller('myctrl', function($scope) {
  var items = [{ "Id": 1, "TypeId": 1, "name": "Michael" },
              { "Id": 2, "TypeId": 2, "name": "Helga" },
              { "Id": 3, "TypeId": 1, "name": "Max" },
              { "Id": 4, "TypeId": 2, "name": "Ann" }];


 $scope.types = [{ "Id": 1, "gender": "Male" },
                 { "Id": 2, "gender": "female" }];
  $scope.filterBy = $scope.types[0];

  var constructViewModel = function(items, types) {
    $scope.mergedItems = [];
    angular.forEach(items, function(i){
      var mergedItem = angular.copy(i);
      var type = types.filter(function(t){return t.Id == i.TypeId})[0];
      mergedItem.gender = type.gender;
      $scope.mergedItems.push(mergedItem);
    });
  }
  
  constructViewModel(items, $scope.types);
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="myctrl">
  <span>Filter by gender: </span><select ng-model="filterBy" ng-options="t as t.gender for t in types"></select>
  <div ng-repeat="item in mergedItems | filter: { gender: filterBy.gender } :true">
    <td>
      <div class="container-fluid">
        <div class="row">
          <div class="text-center">{{ item.name }} {{item.gender}}</div>
        </div>
      </div>
    </td>
  </div>
</div>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

问题是您在错误的位置声明result数组。

function filterByGender() {
    var result = [];
    return function (items, filterBy) {
        ...
        return result;
    }
};

这里的filterByGender函数是创建过滤函数的函数,而从第3行开始的匿名内部函数是实际的过滤函数。 Angular只调用一次filterByGender来构建过滤器,然后在每次输入更改时调用第二个函数。在当前代码中,result数组始终相同(即,在应用程序的生命周期中只存在一个),并且每次调用过滤器时,项目都会再次被推入其中。然后过滤器返回这个完全重复的数组,你从A​​ngular得到错误。

您想要的是每次输入更改时都获得结果,因此应在匿名函数中声明和初始化results,而不是filterByGender。试试这个:

function filterByGender() {
    return function (items, filterBy) {
        var result = [];
        ...
        return result;
    }
};