วิธีการกรองหลายค่า (หรือการดำเนินการ) ใน angularJS


135

ฉันต้องการใช้filterในเชิงมุมและต้องการกรองหลายค่าหากมีค่าใดค่าหนึ่งก็ควรจะแสดง

ฉันมีตัวอย่างโครงสร้างนี้:

วัตถุmovieซึ่งมีคุณสมบัติgenresและผมต้องการที่จะกรองและActionComedy

ฉันรู้ว่าทำได้filter:({genres: 'Action'} || {genres: 'Comedy'})แต่จะทำอย่างไรถ้าฉันต้องการกรองแบบไดนามิก เช่นfilter: variableX

ฉันจะตั้งค่าvariableXใน$scopeเมื่อฉันมีอาร์เรย์ของประเภทที่ฉันต้องกรองได้อย่างไร

ฉันสามารถสร้างมันเป็นสตริงแล้วทำeval()แต่ฉันไม่ต้องการใช้ eval () ...


16
คุณแน่ใจหรือว่าใช้filter:({genres: 'Action'} || {genres: 'Comedy'})งานได้จริง angular 1.3.16มันจะไม่ได้อยู่ใน
twmulloy

4
เท่ากันสำหรับ 1.4.8! มันก็ใช้ไม่ได้ ^^
Alex

คำตอบ:


87

ฉันจะสร้างตัวกรองที่กำหนดเอง พวกเขาไม่ได้ยากขนาดนั้น

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

แม่แบบ:

<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>

แก้ไขที่นี่คือลิงค์: การสร้างตัวกรองเชิงมุม

อัปเดต : นี่คือซอที่มีการสาธิตที่แน่นอนของคำแนะนำของฉัน


ดังนั้นในข้างนอกฉันจะส่งคืนวัตถุภาพยนตร์ที่ฉันต้องการแสดง?
JustGoscha

ขออภัยฉันใช้เวลาสักครู่กว่าจะกลับมาได้ ฉันปรับปรุงคำตอบของฉันด้วยตัวอย่างที่เป็นรูปธรรม
Xesued

โอเคขอบคุณในระหว่างนี้ฉันคิดออกด้วยตัวเอง แต่จะช่วยใครบางคนได้ในภายหลัง :)
JustGoscha

ฉันมีสองค่าเช่นใช้งานและไม่ใช้งานแล้วฉันจะค้นหาโดยใช้ตัวกรองนี้แยกกันได้อย่างไร เราไม่ต้องการสร้างตัวกรองแบบกำหนดเอง
VjyV

81

คุณสามารถใช้ฟังก์ชันคอนโทรลเลอร์เพื่อกรอง

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
นอกจากการแยกข้อกังวลแล้วยังมีเหตุผลใด (ประสิทธิภาพที่ชาญฉลาด) ที่จะหลีกเลี่ยงสิ่งนี้?
Sean Larkin

1
เวอร์ชันจาวาสคริปต์หากผู้อื่นกำลังมองหา:$scope.filteredMovies = $filter('filter')($scope.movies, $scope.filterByGenres);
Evan

23

การสร้างตัวกรองแบบกำหนดเองอาจจะมากเกินไปที่นี่คุณสามารถส่งผ่านตัวเปรียบเทียบแบบกำหนดเองได้หากคุณมีค่าทวีคูณเช่น:

$scope.selectedGenres = "Action, Drama"; 

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

จากนั้นในตัวกรอง:

filter:{name:selectedGenres}:containsComparator

1
ช่วยฉันได้มาก ขอบคุณ
Hristo Enev

@chrismarx ng-modelไฟล์มุมมองจะเป็นอย่างไรเพื่อเชื่อมโยงกับตัวกรองที่อธิบายไว้ (ขออภัยยังคงเรียนรู้ที่นี่) - ฉันชอบความเรียบง่ายของคำตอบของคุณและพยายามที่จะได้รับมันทำงานได้ แต่ไม่แน่ใจว่าจะติดป้ายของฉัน's<input> ng-model:)
twknab

คุณต้องอ่านสิ่งนี้ - docs.angularjs.org/api/ng/filter/filter
chrismarx

18

นี่คือการใช้งานตัวกรองแบบกำหนดเองซึ่งจะกรองข้อมูลโดยใช้อาร์เรย์ของค่ามันจะรองรับวัตถุหลักหลายรายการที่มีทั้งอาร์เรย์และคีย์ค่าเดียว ตามที่กล่าวไว้ inangularJS API AngularJS filter Docรองรับตัวกรองคีย์หลายตัวที่มีค่าเดียว แต่ด้านล่างตัวกรองที่กำหนดเองจะรองรับคุณสมบัติเช่นเดียวกับ angularJS และยังรองรับอาร์เรย์ของค่าและการรวมกันของอาร์เรย์และค่าเดียวของคีย์โปรดดูข้อมูลโค้ดด้านล่าง

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;
}
}]);

การใช้งาน:

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

นี่คือตัวอย่างซอที่ใช้ตัวกรองแบบกำหนดเอง "filterMutiple"ด้านบน ::: ตัวอย่างซอ :::


1
ดูเหมือนคุณจะมีเจตนาที่ถูกต้อง แต่ตัวอย่างของคุณดูเหมือนจะไม่ได้ผลตามที่คาดไว้ ผลลัพธ์ในตารางดูเหมือนจะไม่สอดคล้องกัน
Hultner

สิ่งนี้ใช้ไม่ได้กับ JSON ที่มีวัตถุที่ซ้อนกัน จะเหมาะมากถ้าเป็นเช่นนั้น นอกจากนี้ยังทำให้เกิดlength property undefinedข้อผิดพลาดในคอนโซล
SinSync

มันใช้งานได้สำหรับฉัน แต่จะส่งคืนผลลัพธ์ทั้งหมดหากไม่พบแอตทริบิวต์ที่ค้นหาในรายการฉันจะเปลี่ยนพฤติกรรมนี้และไม่ส่งคืนผลลัพธ์ได้อย่างไรหากไม่มีค่าในรายการ
Zakaria Belghiti

@ZakariaBelghiti หากคุณไม่ต้องการส่งคืนผลลัพธ์ให้เปลี่ยนรหัส: if (fData.length > 0){ this.filteredData = fData; } เป็นเพียง: this.filteredData = fData;
pconnor88

@Gerfried ฉันคิดว่าไซต์นั้นขูด Stackoverflow จริงๆดังนั้นพวกเขาจึงขโมยคำตอบของเขาไม่ใช่ทางอื่น
คริส

13

หากคุณต้องการกรอง Array of Objects คุณสามารถให้

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

คุณสมบัติแต่ละรายการจะถูกกรองโดยตัวกรองเฉพาะที่กำหนดสำหรับคุณสมบัตินั้น

แต่ถ้าคุณต้องการกรองตามคุณสมบัติแต่ละรายการและกรองทั่วโลกสำหรับคุณสมบัติทั้งหมดคุณสามารถทำสิ่งนี้ได้

<tr ng-repeat="supp in $data | filter : filterObject |  filter : search">
โดยที่ "filterObject" คือออบเจ็กต์สำหรับค้นหาคุณสมบัติแต่ละรายการและ "Search" จะค้นหาในทุกคุณสมบัติทั่วโลก

~ Atul


สิ่งนี้ทำงานได้ดีสำหรับฉันและเป็นทางออกที่ดีกว่าคนอื่น ๆ ที่แนะนำให้ถ่ายการกรองไปยังฟังก์ชันเนื่องจาก AngularJS จะจับคู่ข้อความบางส่วนเมื่อกรองสตริง
Snaver

9

ฉันใช้เวลาพอสมควรและขอบคุณ @chrismarx ฉันเห็นว่าค่าเริ่มต้นของเชิงมุมfilterFilterช่วยให้คุณสามารถส่งผ่านตัวเปรียบเทียบของคุณเองได้ นี่คือตัวเปรียบเทียบที่แก้ไขสำหรับหลายค่า:

  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;
    }
  };

และถ้าเราต้องการสร้างตัวกรองแบบกำหนดเองสำหรับ 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);
      };
    });

จากนั้นเราสามารถใช้งานได้ทุกที่ที่เราต้องการ:

$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>

สุดท้ายนี่เป็นplunkr

หมายเหตุ: อาร์เรย์ที่คาดหวังควรมีเฉพาะวัตถุธรรมดาเช่นString , Numberเป็นต้น


ขอบคุณสิ่งนี้ช่วยฉัน หากต้องการตรวจสอบตัวกรองค่าเดียวด้วยหรือ: {name: 'Jack'} หากใครต้องการเช่นเดียวกับฉัน ...
ฮวน

7

คุณสามารถใช้ตัวกรองsearchFieldของ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
คุณยังสามารถใช้ "where" ของ angular.filter <tr ng-repeat = "obj ในคอลเลกชัน | where: {id: 1}"> {{obj.name}} </tr>
hcarreras

1
searchหยุดที่นี่
Aakash

1
ฉันลองสิ่งนี้และตั้งค่าทั้งโมดูลในแอพเชิงมุมและสคริปต์ในดัชนี HTML ของฉัน แต่ฉันยังคงสามารถค้นหาฟิลด์ทั้งหมดไม่ใช่เฉพาะฟิลด์ที่ฉันระบุ :(
twknab

7

คุณยังสามารถใช้ได้ngIfหากสถานการณ์เอื้ออำนวย:

<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
ทางออกที่ดีและเรียบง่าย!
Leopoldo Sanczyk

7

วิธีแก้ปัญหาที่เร็วที่สุดที่ฉันพบคือใช้filterByตัวกรองจากangular-filterตัวอย่างเช่น:

<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>

ข้อดีก็คือangular-filterเป็นไลบรารีที่ได้รับความนิยมพอสมควร (~ 2.6k stars บน GitHub) ซึ่งยังคงได้รับการพัฒนาและบำรุงรักษาอย่างต่อเนื่องดังนั้นจึงควรเพิ่มลงในโครงการของคุณในรูปแบบการอ้างอิง


1
คำตอบที่ดีที่สุด ขอบคุณ.
Daniel Gruszczyk

4

ฉันเชื่อว่านี่คือสิ่งที่คุณกำลังมองหา:

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

1
เพียงแค่เชื่อมอาร์เรย์เข้าด้วยกันและไม่ส่งคืนการรวมกัน
muaaz

เว้นแต่ว่าฉันเข้าใจผิดในการใช้งานฉันไม่เห็นว่านั่นเป็นปัญหาอย่างไร
jKlaus

2

โปรดลองทำตามนี้

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

สมมติว่าคุณมีอาร์เรย์สองอาร์เรย์หนึ่งรายการสำหรับภาพยนตร์และอีกหนึ่งรายการสำหรับประเภท

เพียงใช้ตัวกรองเป็น: filter:{genres: genres.type}

ประเภทที่นี่คืออาร์เรย์และประเภทมีค่าสำหรับประเภท


1

ฉันเขียนสิ่งนี้สำหรับสตริงและฟังก์ชันการทำงาน (ฉันรู้ว่าไม่ใช่คำถาม แต่ฉันค้นหามันและมาถึงที่นี่) อาจจะขยายได้

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;
    });  
  };
});

การใช้งาน:

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


1

ฉันมีสถานการณ์ที่คล้ายกัน การเขียนตัวกรองที่กำหนดเองได้ผลสำหรับฉัน หวังว่านี่จะช่วยได้!

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

นี่คือตัวอย่างของฉันในการสร้างตัวกรองและคำสั่งสำหรับตาราง jsfiddle

คำสั่งรับรายการ (ข้อมูล) และสร้างตารางด้วยตัวกรอง

<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>

ยินดีถ้าฉันช่วยคุณ


1

สายเกินไปที่จะเข้าร่วมปาร์ตี้ แต่อาจช่วยใครบางคนได้:

เราสามารถทำได้ในสองขั้นตอนขั้นแรกกรองตามคุณสมบัติแรกแล้วต่อด้วยตัวกรองที่สอง:

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

ดูซอที่ใช้งานได้พร้อมตัวกรองคุณสมบัติหลายรายการ


0

ตัวเลือกที่ 1: การใช้พารามิเตอร์ตัวเปรียบเทียบตัวกรองเชิงมุม

// 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);

ตัวเลือกที่ 2: การใช้การปฏิเสธตัวกรองเชิงมุมที่พิสูจน์แล้ว

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'});


-14

คำตอบที่ดีที่สุดคือ:

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

5
นี่ไม่ใช่แค่กรอง 'ตลก'?
user1135469

2
ฉันลองแล้วมันกรองเฉพาะ "ตลก" ไม่ใช่ "การกระทำ"
เบ็น
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.