Cách lọc nhiều giá trị (thao tác OR) trong angularJS


135

Tôi muốn sử dụng filtertrong góc và muốn lọc cho nhiều giá trị, nếu nó có một trong các giá trị thì nó sẽ được hiển thị.

Tôi có ví dụ về cấu trúc này:

Một đối tượng moviecó thuộc tính genresvà tôi muốn lọc ActionComedy.

Tôi biết tôi có thể làm filter:({genres: 'Action'} || {genres: 'Comedy'}), nhưng phải làm gì nếu tôi muốn lọc nó một cách linh hoạt. Ví dụfilter: variableX

Làm cách nào để đặt variableXtrong $scope, khi tôi có một loạt các thể loại tôi phải lọc?

Tôi có thể xây dựng nó thành một chuỗi và sau đó thực hiện eval()nhưng tôi không muốn sử dụng eval () ...


16
Bạn có chắc chắn rằng filter:({genres: 'Action'} || {genres: 'Comedy'})thậm chí hoạt động? nó không trong angular 1.3.16.
twmulloy

4
tương tự cho 1.4.8! nó không hoạt động ^^
Alex

Câu trả lời:


87

Tôi sẽ chỉ tạo một bộ lọc tùy chỉnh. Chúng không khó lắm đâu.

angular.module('myFilters', []).
  filter('bygenre', function() {
    return function(movies,genres) {
      var out = [];
      // Filter logic here, adding matches to the out var.
      return out;
    }
  });

bản mẫu:

<h1>Movies</h1>

<div ng-init="movies = [
          {title:'Man on the Moon', genre:'action'},
          {title:'Meet the Robinsons', genre:'family'},
          {title:'Sphere', genre:'action'}
       ];" />
<input type="checkbox" ng-model="genrefilters.action" />Action
<br />
<input type="checkbox" ng-model="genrefilters.family" />Family
<br />{{genrefilters.action}}::{{genrefilters.family}}
<ul>
    <li ng-repeat="movie in movies | bygenre:genrefilters">{{movie.title}}: {{movie.genre}}</li>
</ul>

Chỉnh sửa ở đây là liên kết: Tạo bộ lọc góc

CẬP NHẬT : Đây là một fiddle có một bản demo chính xác của đề nghị của tôi.


Vì vậy, trong ra ngoài tôi trả lại các đối tượng phim mà tôi muốn hiển thị?
JustGoscha

Xin lỗi, tôi phải mất một lúc để quay lại. Tôi đã cập nhật câu trả lời của mình với một ví dụ cụ thể.
Xesued

ok, cảm ơn, trong lúc này tôi đã tự mình tìm ra, nhưng chắc chắn nó sẽ giúp được ai đó sau này :)
JustGoscha

Hoặc tôi có hai giá trị như hoạt động và không hoạt động thì làm sao tôi có thể tìm kiếm riêng biệt bằng bộ lọc này. chúng tôi không muốn tạo bộ lọc tùy chỉnh.
VjyV

80

Bạn có thể sử dụng chức năng điều khiển để lọc.

function MoviesCtrl($scope) {

    $scope.movies = [{name:'Shrek', genre:'Comedy'},
                     {name:'Die Hard', genre:'Action'},
                     {name:'The Godfather', genre:'Drama'}];

    $scope.selectedGenres = ['Action','Drama'];

    $scope.filterByGenres = function(movie) {
        return ($scope.selectedGenres.indexOf(movie.genre) !== -1);
    };

}

HTML:

<div ng-controller="MoviesCtrl">
    <ul>
        <li ng-repeat="movie in movies | filter:filterByGenres">
            {{ movie.name }} {{ movie.genre }}
        </li>
    </ul>
</div>

1
Bên cạnh việc phân tách các mối quan tâm, có lý do nào (hiệu suất khôn ngoan) mà điều này sẽ tránh được không?
Sean Larkin

1
phiên bản javascript nếu người khác đang tìm kiếm nó:$scope.filteredMovies = $filter('filter')($scope.movies, $scope.filterByGenres);
Evan

23

Tạo bộ lọc tùy chỉnh có thể là quá mức cần thiết ở đây, bạn chỉ có thể chuyển qua một bộ so sánh tùy chỉnh, nếu bạn có nhiều giá trị như:

$scope.selectedGenres = "Action, Drama"; 

$scope.containsComparator = function(expected, actual){  
  return actual.indexOf(expected) > -1;
};

sau đó trong bộ lọc:

filter:{name:selectedGenres}:containsComparator

1
Giúp tôi rất nhiều. Cảm ơn
Hristo Enev

@chrismarx Điều gì sẽ ng-modelcó trong tệp xem để liên kết với bộ lọc được mô tả? (xin lỗi, vẫn đang học tại đây) - Tôi như sự đơn giản của câu trả lời của bạn và cố gắng để làm cho nó làm việc nhưng không chắc chắn làm thế nào để đặt tên của tôi <input>'s ng-model. :)
twknab

bạn cần đọc cái này - docs.angularjs.org/api/ng/filter/filter
chrismarx

18

Dưới đây là việc triển khai bộ lọc tùy chỉnh, sẽ lọc dữ liệu bằng cách sử dụng mảng các giá trị. Nó sẽ hỗ trợ nhiều đối tượng khóa với cả mảng và giá trị đơn của các khóa. Như đã đề cập inangularJS API Bộ lọc AngularJS Doc hỗ trợ nhiều bộ lọc khóa với một giá trị, nhưng bộ lọc tùy chỉnh bên dưới sẽ hỗ trợ tính năng tương tự như angularJS và cũng hỗ trợ mảng các giá trị và kết hợp cả mảng và giá trị đơn của khóa. Vui lòng tìm đoạn mã bên dưới,

myApp.filter('filterMultiple',['$filter',function ($filter) {
return function (items, keyObj) {
    var filterObj = {
        data:items,
        filteredData:[],
        applyFilter : function(obj,key){
            var fData = [];
            if (this.filteredData.length == 0)
                this.filteredData = this.data;
            if (obj){
                var fObj = {};
                if (!angular.isArray(obj)){
                    fObj[key] = obj;
                    fData = fData.concat($filter('filter')(this.filteredData,fObj));
                } else if (angular.isArray(obj)){
                    if (obj.length > 0){
                        for (var i=0;i<obj.length;i++){
                            if (angular.isDefined(obj[i])){
                                fObj[key] = obj[i];
                                fData = fData.concat($filter('filter')(this.filteredData,fObj));    
                            }
                        }

                    }
                }
                if (fData.length > 0){
                    this.filteredData = fData;
                }
            }
        }
    };
    if (keyObj){
        angular.forEach(keyObj,function(obj,key){
            filterObj.applyFilter(obj,key);
        });
    }
    return filterObj.filteredData;
}
}]);

Sử dụng:

arrayOfObjectswithKeys | filterMultiple:{key1:['value1','value2','value3',...etc],key2:'value4',key3:[value5,value6,...etc]}

Dưới đây là một ví dụ thú vị với việc thực hiện bộ lọc tùy chỉnh "filterMutipl" ở trên . ::: Ví dụ Fiddle :::


1
Bạn dường như có ý định chính xác nhưng ví dụ của bạn dường như không hoạt động như mong đợi. Đầu ra trong bảng có vẻ không nhất quán.
Hultner

Điều này không hoạt động với JSON với các đối tượng lồng nhau. Sẽ là lý tưởng nếu nó đã làm. Ngoài ra, nó bắn một length property undefinedlỗi trong giao diện điều khiển.
SinSync

Nó hoạt động với tôi nhưng nó trả về tất cả các kết quả nếu nó không tìm thấy thuộc tính được tìm kiếm trong danh sách, làm cách nào để đảo ngược hành vi này và không trả lại kết quả nếu giá trị không tồn tại trong danh sách?
Zakaria Belghiti

@ZakariaBelghiti nếu bạn muốn trả về không có kết quả, sau đó thay đổi mã: if (fData.length > 0){ this.filteredData = fData; } thành: this.filteredData = fData;
pconnor88 18/03/2016

@Gerfried Tôi nghĩ rằng trang web đó thực sự loại bỏ Stackoverflow, vì vậy họ đã đánh cắp câu trả lời của anh ấy, không phải cách khác
Chris

13

Nếu bạn muốn lọc trên Mảng đối tượng thì bạn có thể cung cấp

filter:({genres: 'Action', key :value }.

Thuộc tính riêng lẻ sẽ được lọc theo bộ lọc cụ thể được cung cấp cho thuộc tính đó.

Nhưng nếu bạn muốn một cái gì đó như bộ lọc theo Thuộc tính riêng lẻ và bộ lọc toàn cầu cho tất cả các thuộc tính thì bạn có thể làm một cái gì đó như thế này.

<tr ng-repeat="supp in $data | filter : filterObject |  filter : search">
Trong đó "filterObject" là một đối tượng để tìm kiếm một thuộc tính riêng lẻ và "Tìm kiếm" sẽ tìm kiếm trong mọi thuộc tính trên toàn cầu.

~ Atul


Điều này làm việc tốt với tôi và là một giải pháp tốt hơn so với những người khác đề xuất giảm tải bộ lọc cho một chức năng vì AngularJS không khớp một phần văn bản khi lọc trên chuỗi.
Snaver

9

Tôi đã dành một chút thời gian cho nó và nhờ @chrismarx, tôi thấy mặc định của góc đó filterFiltercho phép bạn vượt qua bộ so sánh của riêng bạn. Đây là bộ so sánh được chỉnh sửa cho nhiều giá trị:

  function hasCustomToString(obj) {
        return angular.isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
  }
  var comparator = function (actual, expected) {
    if (angular.isUndefined(actual)) {
      // No substring matching against `undefined`
      return false;
    }
    if ((actual === null) || (expected === null)) {
      // No substring matching against `null`; only match against `null`
      return actual === expected;
    }
    // I edited this to check if not array
    if ((angular.isObject(expected) && !angular.isArray(expected)) || (angular.isObject(actual) && !hasCustomToString(actual))) {
      // Should not compare primitives against objects, unless they have custom `toString` method
      return false;
    }
    // This is where magic happens
    actual = angular.lowercase('' + actual);
    if (angular.isArray(expected)) {
      var match = false;
      expected.forEach(function (e) {
        e = angular.lowercase('' + e);
        if (actual.indexOf(e) !== -1) {
          match = true;
        }
      });
      return match;
    } else {
      expected = angular.lowercase('' + expected);
      return actual.indexOf(expected) !== -1;
    }
  };

Và nếu chúng tôi muốn tạo bộ lọc tùy chỉnh cho DRY:

angular.module('myApp')
    .filter('filterWithOr', function ($filter) {
      var comparator = function (actual, expected) {
        if (angular.isUndefined(actual)) {
          // No substring matching against `undefined`
          return false;
        }
        if ((actual === null) || (expected === null)) {
          // No substring matching against `null`; only match against `null`
          return actual === expected;
        }
        if ((angular.isObject(expected) && !angular.isArray(expected)) || (angular.isObject(actual) && !hasCustomToString(actual))) {
          // Should not compare primitives against objects, unless they have custom `toString` method
          return false;
        }
        console.log('ACTUAL EXPECTED')
        console.log(actual)
        console.log(expected)

        actual = angular.lowercase('' + actual);
        if (angular.isArray(expected)) {
          var match = false;
          expected.forEach(function (e) {
            console.log('forEach')
            console.log(e)
            e = angular.lowercase('' + e);
            if (actual.indexOf(e) !== -1) {
              match = true;
            }
          });
          return match;
        } else {
          expected = angular.lowercase('' + expected);
          return actual.indexOf(expected) !== -1;
        }
      };
      return function (array, expression) {
        return $filter('filter')(array, expression, comparator);
      };
    });

Và sau đó chúng ta có thể sử dụng nó bất cứ nơi nào chúng ta muốn:

$scope.list=[
  {name:'Jack Bauer'},
  {name:'Chuck Norris'},
  {name:'Superman'},
  {name:'Batman'},
  {name:'Spiderman'},
  {name:'Hulk'}
];


<ul>
  <li ng-repeat="item in list | filterWithOr:{name:['Jack','Chuck']}">
    {{item.name}}
  </li>
</ul>

Cuối cùng, đây là một plunkr .

Lưu ý: Mảng dự kiến ​​chỉ nên chứa các đối tượng đơn giản như Chuỗi , Số , v.v.


Cảm ơn điều này đã giúp tôi. Để kiểm tra bộ lọc giá trị duy nhấtWithOr: {name: 'Jack'} Nếu có ai cần giống như tôi ...
Juan

7

bạn có thể sử dụng bộ lọc searchField của angular.filter

JS:

$scope.users = [
 { first_name: 'Sharon', last_name: 'Melendez' },
 { first_name: 'Edmundo', last_name: 'Hepler' },
 { first_name: 'Marsha', last_name: 'Letourneau' }
];

HTML:

<input ng-model="search" placeholder="search by full name"/> 
<th ng-repeat="user in users | searchField: 'first_name': 'last_name' | filter: search">
  {{ user.first_name }} {{ user.last_name }}
</th>
<!-- so now you can search by full name -->

3
Bạn cũng có thể sử dụng "where" của angular.filter <tr ng-repeat = "obj trong bộ sưu tập | trong đó: {id: 1}"> {{obj.name}} </ tr>
hcarrera

1
các searchđiểm dừng ở đây.
Akash

1
Tôi đã thử điều này và thiết lập cả mô-đun trong ứng dụng góc cạnh và tập lệnh trong HTML chỉ mục của mình nhưng tôi vẫn có thể tìm kiếm TẤT CẢ các trường, không chỉ trường tôi đã chỉ định :(
twknab

7

Bạn cũng có thể sử dụng ngIfnếu tình huống cho phép:

<div ng-repeat="p in [
 { name: 'Justin' }, 
 { name: 'Jimi' }, 
 { name: 'Bob' }
 ]" ng-if="['Jimi', 'Bob'].indexOf(e.name) > -1">
 {{ p.name }} is cool
</div>

1
Giải pháp đẹp và đơn giản!
Leopoldo Sanchot

7

Giải pháp nhanh nhất mà tôi đã tìm thấy là sử dụng filterBybộ lọc từ bộ lọc góc , ví dụ:

<input type="text" placeholder="Search by name or genre" ng-model="ctrl.search"/>   
<ul>
  <li ng-repeat="movie in ctrl.movies | filterBy: ['name', 'genre']: ctrl.search">
    {{movie.name}} ({{movie.genre}}) - {{movie.rating}}
  </li>
</ul>

Ưu điểm là bộ lọc góc là một thư viện khá phổ biến (~ 2,6k sao trên GitHub) vẫn được tích cực phát triển và duy trì, do đó, sẽ rất tốt nếu thêm nó vào dự án của bạn như một sự phụ thuộc.


1
Cho đến nay câu trả lời tốt nhất. Cảm ơn.
Daniel Gruszchot

4

Tôi tin rằng đây là những gì bạn đang tìm kiếm:

<div>{{ (collection | fitler1:args) + (collection | filter2:args) }}</div>

1
Nó chỉ nối các mảng và không trả về một liên minh.
muaaz

Trừ khi tôi hiểu nhầm việc thực hiện, tôi không thấy đó là một vấn đề.
jKlaus

2

Hãy thử nó

var m = angular.module('yourModuleName');
m.filter('advancefilter', ['$filter', function($filter){
    return function(data, text){
        var textArr = text.split(' ');
        angular.forEach(textArr, function(test){
            if(test){
                  data = $filter('filter')(data, test);
            }
        });
        return data;
    }
}]);

2

Giả sử bạn có hai mảng, một cho phim và một cho thể loại

Chỉ cần sử dụng bộ lọc như: filter:{genres: genres.type}

Ở đây thể loại là mảng và loại có giá trị cho thể loại


1

Tôi đã viết cái này cho chuỗi VÀ chức năng (tôi biết đó không phải là câu hỏi nhưng tôi đã tìm kiếm nó và đến đây), có lẽ nó có thể được mở rộng.

String.prototype.contains = function(str) {
  return this.indexOf(str) != -1;
};

String.prototype.containsAll = function(strArray) {
  for (var i = 0; i < strArray.length; i++) {
    if (!this.contains(strArray[i])) {
      return false;
    }
  }
  return true;
}

app.filter('filterMultiple', function() {
  return function(items, filterDict) {
    return items.filter(function(item) {
      for (filterKey in filterDict) {
        if (filterDict[filterKey] instanceof Array) {
          if (!item[filterKey].containsAll(filterDict[filterKey])) {
            return false;
          }
        } else {
          if (!item[filterKey].contains(filterDict[filterKey])) {
            return false;
          }
        }
      }
      return true;
    });  
  };
});

Sử dụng:

<li ng-repeat="x in array | filterMultiple:{key1: value1, key2:[value21, value22]}">{{x.name}}</li>


1

Tôi đã có tình huống tương tự. Viết bộ lọc tùy chỉnh làm việc cho tôi. Hi vọng điêu nay co ich!

JS:

App.filter('searchMovies', function() {
    return function (items, letter) {
        var resulsts = [];
        var itemMatch = new RegExp(letter, 'i');
        for (var i = 0; i < items.length; i++) {
            var item = items[i];
            if ( itemMatch.test(item.name) || itemMatch.test(item.genre)) {
                results.push(item);
            }
        }
        return results;
    };
});

HTML:

<div ng-controller="MoviesCtrl">
    <ul>
        <li ng-repeat="movie in movies | searchMovies:filterByGenres">
            {{ movie.name }} {{ movie.genre }}
        </li>
    </ul>
</div>

1

Dưới đây là ví dụ của tôi về cách tạo bộ lọc và chỉ thị cho bảng jsfiddle

danh sách lấy chỉ thị (dữ liệu) và tạo bảng với các bộ lọc

<div ng-app="autoDrops" ng-controller="HomeController">
<div class="row">
    <div class="col-md-12">
        <h1>{{title}}</h1>
        <ng-Multiselect array-List="datas"></ng-Multiselect>
    </div>
</div>
</div>

niềm vui của tôi nếu tôi giúp bạn


1

Quá muộn để tham gia bữa tiệc nhưng có thể nó có thể giúp ai đó:

Chúng ta có thể thực hiện theo hai bước, bộ lọc đầu tiên theo thuộc tính đầu tiên và sau đó ghép theo bộ lọc thứ hai:

$scope.filterd = $filter('filter')($scope.empList, { dept: "account" });
$scope.filterd = $scope.filterd.concat($filter('filter')($scope.empList, { dept: "sales" }));  

Xem fiddle làm việc với nhiều bộ lọc thuộc tính


0

TÙY CHỌN 1: Sử dụng tham số bộ so sánh bộ lọc được cung cấp góc

// declaring a comparator method
$scope.filterBy = function(actual, expected) {
    return _.contains(expected, actual); // uses underscore library contains method
};

var employees = [{name: 'a'}, {name: 'b'}, {name: 'c'}, {name: 'd'}];

// filter employees with name matching with either 'a' or 'c'
var filteredEmployees = $filter('filter')(employees, {name: ['a','c']}, $scope.filterBy);

TÙY CHỌN 2: Sử dụng phủ định bộ lọc được cung cấp góc

var employees = [{name: 'a'}, {name: 'b'}, {name: 'c'}, {name: 'd'}];

// filter employees with name matching with either 'a' or 'c'
var filteredEmployees = $filter('filter')($filter('filter')(employees, {name: '!d'}), {name: '!b'});

-3

Giải pháp của tôi

ng-repeat="movie in movies | filter: {'Action'} + filter: {'Comedy}"

đã kiểm tra trên VS 2015, nó không nhận được cú pháp và nó cũng không hoạt động
WholeLifeLearner

-15

câu trả lời tốt nhất là:

filter:({genres: 'Action', genres: 'Comedy'}

5
Điều này không chỉ lọc trên 'Hài kịch'?
dùng1135469

2
Tôi đã thử điều này và nó chỉ lọc trên 'Hài kịch', không phải trên 'Hành động'.
Ben
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.