AngularJS:在除一个字段之外的所有字段的控制器中进行过滤

时间:2015-02-26 14:46:08

标签: javascript angularjs

在我的angularjs应用程序中,我通过json获得了这样的数据示例:

{"id":"1a", "name": "aaa", "emails": {{"123@123.com"}, {"123@123.info"}}},
{"id":"2a", "name": "aba", "emails": {{"23@123.com"}, {"3@123.info"}}},
{"id":"3a", "name": "aab", "emails": {{"3@123.com"}, {"3@123.info"}}},

出于性能原因,我没有使用过滤器进行ng-repeat,但使用ng-show模式...

所以在控制器中我有这样的代码(搜索是我的输入值):

  $scope.$watch('search', function(newVal, oldVal) {
    $scope.filteredArray = $filter('filter')($scope.users, $scope.search, newVal);
  });

但它甚至在id中搜索,例如我输入a并从id获取它,但我不需要字段ID ...

那么如何仅在特定字段中使用过滤器进行搜索?

5 个答案:

答案 0 :(得分:5)

如果你关心性能,可能最好的选择就是不要使用$filter('filter')。假设您的数据是JSON,您可以执行以下操作:

function contains(src, value, except) {
  var key;
  switch(typeof src) {
    case 'string':
    case 'number':
    case 'boolean':
      return String(src).indexOf(value) > -1;
    case 'object':
      except = except || [];
      for(key in src) {
        if( src.hasOwnProperty(key) &&
            except.indexOf(key) < 0 &&
            contains(src[key], value, except)
        ) {
          return true;
        }
      }
  }
  return false;
}

angular.module('app', []).
  factory('appService', function() {
    var data = [
      {"id":"1a", "name": "aaa", "emails": ["123@123.com", "123@123.info"]},
      {"id":"2a", "name": "aba", "emails": ["23@123.com", "3@123.info"]},
      {"id":"3a", "name": "aab", "emails": ["3@123.com", "3@123.info", ["1@123.com", "2@123.info"]]}
    ];
    return {
      getFilteredData: function(filter, except) {
        return filter ? data.filter(function(item) {
          return contains(item, filter, except)
        }) : data.slice(0);
      }
    }
  }).
  controller('appController', ['$scope', 'appService', function($scope, appService) {
    $scope.search = '';
    $scope.$watch('search', function(val) {
      $scope.data = appService.getFilteredData(val, ['id']);
    });
  }]);
<!DOCTYPE html>
<html ng-app="app">

  <head>
    <script src="https://code.angularjs.org/1.4.0-beta.6/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-controller="appController">
    <input ng-model="search" ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"/>
    <ul>
      <li ng-repeat="item in data">{{::item}}</li>
    </ul>
  </body>

</html>

上面的代码将在所有数据字段中进行深度搜索,并且不包括作为参数传递的字段列表的项目(在此示例中为['id']

提示#1:在Angular中实现业务逻辑是一种不好的做法,它为此目的提供服务。

提示#2:如果要显示大型数据集,从性能的角度来看,debouncing过滤器更改将非常有用。

提示#3:如果您的数据集项是不可变的,您可以利用one-time binding(使用{{::item}}语法)并通过减少Angular创建的手表数来加速您的应用。

答案 1 :(得分:2)

  

已经给出了几个答案,并给出了很多提示。我的贡献不会总结其他人的工作,而只是提到另一种使用angularjs过滤数据的方法,并考虑到性能:使用极速的第三方过滤工具


是的,在带有x in data | myFilter的ng-repeat中使用过滤器非常适合阅读,但是对于大型集合执行效果不佳,因为重复评估过滤器。

对于大型集合,最好交换性能的可读性,并在控制器中进行过滤,如OP中所述。


现在,如果您要在控制器中进行过滤以获得性能,那么您最好不要重新发明轮子,并使用现有的过滤工具。

这是一个带有简单工具箱的plunker,立即过滤 50万文档的集合。

我几个月前写过它,因为当我使用自定义或内置的angularjs过滤器时,过滤3000多个文档的集合会减慢我的应用程序。

filter a large collection using an extremely fast third-party tool: crossFilter

现在在框中输入一个数字:过滤(几乎)是即时的。事实上,它不是即时的原因是因为我故意引入了750毫秒的延迟,给用户留出时间进行打字,而不是在每次按键时过滤: var filterTextTimeout=750;

编辑:正如评论中正确提到的,使用angularjs版本&gt; 1.2时,应使用debounce的{​​{1}}选项代替ng-model < / p>


使用的过滤工具是crossFilter,还有ng-crossfilter

它可以如此快速的原因是它(很快)创建了自己的排序索引。

它出现many features,如RegExp,InArray过滤器,模糊过滤器,map-reduce等......

答案 2 :(得分:1)

您可以通过在您感兴趣的每个字段上调用过滤器功能来自定义过滤器expression

angular.module("app", []).controller("ctrl", ["$scope", "$filter", function ($scope, $filter) {
    $scope.users = [{
        "id": "1a",
            "name": "aaa",
            "emails": ["123@123.com", "123@123.info"]
    }, {
        "id": "2a",
            "name": "aba",
            "emails": ["23@123.com", "3@123.info"]
    }, {
        "id": "3a",
            "name": "aab",
            "emails": ["3@123.com", "3@123.info"]
    }];

    $scope.$watch('search', function (newVal, oldVal) {
        //use function to implement a specific expression
        var expression = function (value, index) {
            fields = ['name', 'emails'];
            for (var i in fields) {
                field = fields[i];
                query = {}; //create an expression
                query[field] = $scope.search;
                r = $filter('filter')([value], query);
                if (r.length > 0) return true;
            }
            return false;
        }
        $scope.filteredArray = $filter('filter')($scope.users, expression)
    });
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
    <div ng-controller="ctrl">Search:
        <input type="text" ng-model="search">
        <div ng-repeat="x in filteredArray">{{x}}</div>
    </div>
</div>
  

答案 3 :(得分:0)

如果您可以将字段名称更改为以$开头的名称,则Angular将在过滤中跳过此字段。将https://next.plnkr.co/edit/TjSdGICBfcR8WDyVhttps://docs.angularjs.org/api/ng/filter/filter的原始示例进行比较。我在$ name中添加了$,现在| filter在使用$name的全局过滤中跳过了字段search.$

答案 4 :(得分:-1)

过滤器的比较器可以是镜像数组中对象的对象。如果是这种情况,它将仅基于现有属性进行过滤。也就是说,如果newVal是以下形式的对象:

{
  name: someVal,
  emails: otherVal
}

您只会在nameemails上进行过滤,而不是id。将id字段设为空白也很好(这意味着您不会按id搜索。)

这是一个例子:

&#13;
&#13;
angular.module("app", []).controller("ctrl", ["$scope", "$filter", function($scope, $filter){
  $scope.data = [
    {"id":"1a", "name": "aaa", "emails": ["123@123.com", "123@123.info"]},
    {"id":"2a", "name": "aba", "emails": ["23@123.com", "3@123.info"]},
    {"id":"3a", "name": "aab", "emails": ["3@123.com", "3@123.info"]}
  ];
}]);
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
  <div ng-controller="ctrl">
    Name: <input type="text" ng-model="filter.name"><br>
    Email: <input type="text" ng-model="filter.emails">
    <div ng-repeat="x in data | filter:filter">
      {{x}}
    </div>
  </div>
</div> 
  
&#13;
&#13;
&#13;

请注意,如何在电子邮件输入中输入值将正确搜索数组。如果需要,还可以将两个输入设置为同一模型,然后将该模型设置为filter.namefilter.email

//html
Search Query: <input type="text" ng-model="query">
//js
$scope.$watch('query', function(newVal, oldVal){
    $scope.filter.name=newVal;
    $scope.filter.emails=newVal;
}