Angular Filters: How to pre-filter so angular filters consider only a partial of the whole data object

对着背影说爱祢 提交于 2019-12-10 21:56:51

问题


I got a large data table (which gets its data from a json api) with multiple columns and would like to implement multiple filters doing the following:

  1. an option to select which data-column should be considered (a dropdown containing the thead option) [my columnFilter] and then
  2. an input field to filter that particular data-partial [my searchFilter]

I've got the searchFilter working, but I'm not sure how to connect the columnFilter and make the searchFilter only apply to the selected data-partial.

So let's say I'd like to only see Descriptions containing the world "blue".

How can I bind these two filters and make this work?

Here is some of my code:

  Select data column:
  <select ng-model="columnFilter" ng-options="heading for heading in headings">
  </select>
</div>
<div class="col-sm-12">
   Filter selection: <input type='text' ng-model="searchFilter" />
</div>
<table class="table table-bordered">
    <thead>
      <tr>
        <th>URL</th>
        <th>Title</th>
        <th>Traffic</th>
        <th>Description</th>
        <th>ID</th>
      </tr>
  </thead>
  <tbody ng-repeat="url in urls | filter:searchFilter">
    <tr>
      <td>{{url.url}}</td>
      <td>{{url.title}}</td>
      <td>{{url.traffic}}</td>
      <td>{{url.descr}}</td>
      <td>{{url.id}}</td>
    </tr>
    </tbody>
</table>

and a link to a working plunker: http://plnkr.co/edit/TddllGiey0RmCx18eVdd?p=preview


回答1:


As far as i know angular, there is two main way to solve your problem.

  1. define a custom a filter
  2. define a function to filter your data and call it with ng-show or ng-hide

I got a large data table

If i understand well, you specify that because performance is a big issue.

There is a good article which exposes the differences between those two solutions : http://www.bennadel.com/blog/2487-filter-vs-nghide-with-ngrepeat-in-angularjs.htm

As performance may be really important for you, i suggest you the second approach.

In your view:

<tbody ng-repeat="url in urls" ng-show="filterUrl(url)">

In your controller:

$scope.searchFilter = "";
$scope.columnFilter = $scope.headings[5];

$scope.filterUrl = function(url){ 
  if(!$scope.searchFilter || $scope.searchFilter == "")
    return url;
  var searchFilter= $scope.searchFilter.toLowerCase();
  var trafficString = url.traffic.toString();
  var idString = url.traffic.toString();
  switch($scope.columnFilter){
    case $scope.headings[0]:
      return url.title.toLowerCase().indexOf(searchFilter) != -1;
    case $scope.headings[1]:
      return url.url.toLowerCase().indexOf(searchFilter) != -1;
    case $scope.headings[2]:
      return trafficString.indexOf(searchFilter) != -1;
    case $scope.headings[3]:
      return url.descr.toLowerCase().indexOf(searchFilter) != -1;
    case $scope.headings[4]:
      return idString.indexOf(searchFilter) != -1;
    case $scope.headings[5]: 
      return url.title.toLowerCase().indexOf(searchFilter) != -1 ||
      url.url.toLowerCase().indexOf(searchFilter) != -1 ||
      trafficString.indexOf(searchFilter) != -1 ||
      url.descr.toLowerCase().indexOf(searchFilter) != -1 ||
      idString.indexOf(searchFilter) != -1;
  }
};

Update: If you choose the first approach:

In your view:

 <tbody ng-repeat="url in urls | filterByColumn: searchFilter :columnFilter">

The filter:

app.filter('filterByColumn', function(){
  return function(urls, text, columnFilter){
    var processed = [];
     if(!text || text == "")
        return urls;
    urls.forEach(function(url){
      var searchFilter= text.toLowerCase();
      var trafficString = url.traffic.toString();
      var idString = url.traffic.toString();
      switch(columnFilter){
        case "Title":
          if( url.title.toLowerCase().indexOf(searchFilter) != -1)
            processed.push(url);
          break;
        case "Url":
          if(url.url.toLowerCase().indexOf(searchFilter) != -1)
            processed.push(url);
          break;
        case "Traffic":
          if(trafficString.indexOf(searchFilter) != -1)
            processed.push(url);
          break;
        case "Description":
          if(url.descr.toLowerCase().indexOf(searchFilter) != -1)
            processed.push(url);
          break;
        case "Id":
          if( idString.indexOf(searchFilter) != -1)
            processed.push(url);
          break;
        case "All": 
          if( url.title.toLowerCase().indexOf(searchFilter) != -1 ||
          url.url.toLowerCase().indexOf(searchFilter) != -1 ||
          trafficString.indexOf(searchFilter) != -1 ||
          url.descr.toLowerCase().indexOf(searchFilter) != -1 ||
          idString.indexOf(searchFilter) != -1)
            processed.push(url);
          break;
      }
    });
    return processed;
  };
});

Here is a plunkr: http://plnkr.co/edit/xCwI2AURFpvb6xHgYHxS?p=preview




回答2:


Check how this example straight from the angular page of a creating a custom filter. https://docs.angularjs.org/guide/filter

You can see here that you can create a filter that accepts 2 inputs, the actual object and a parameter. By passing the column name as parameter, you can narrow down your search for the correct rows.




回答3:


I would recommend you to write simple custom filter like this.

.filter('myfilter', function(){
  return function(input, column, text){
    if (!input || !column || !text) return input;
    return input.filter(function(item){
      var value = item[column.toLowerCase()];
      if (!value)return false;
      return value.indexOf(text)>-1;
    });
  };
});

You can use this filter in html.

ng-repeat="url in urls | myfilter:columnFilter:searchFilter"

This is plunker.



来源:https://stackoverflow.com/questions/29273567/angular-filters-how-to-pre-filter-so-angular-filters-consider-only-a-partial-of

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!