So filtern Sie mehrere Werte (ODER-Verknüpfung) in angleJS


135

Ich möchte den filterWinkel verwenden und nach mehreren Werten filtern. Wenn einer der Werte vorhanden ist, sollte er angezeigt werden.

Ich habe zum Beispiel diese Struktur:

Ein Objekt moviewelches die Eigenschaft , genresund ich möchte Filter für Actionund Comedy.

Ich weiß, dass ich es kann filter:({genres: 'Action'} || {genres: 'Comedy'}), aber was tun, wenn ich es dynamisch filtern möchte? Z.Bfilter: variableX

Wie stelle ich variableXdas ein $scope, wenn ich eine Reihe von Genres habe, die ich filtern muss?

Ich könnte es als String konstruieren und dann einen machen, eval()aber ich möchte eval () nicht verwenden ...


16
Bist du sicher, dass das filter:({genres: 'Action'} || {genres: 'Comedy'})überhaupt funktioniert? es geht nicht rein angular 1.3.16.
Twmulloy

4
Gleiches gilt für 1.4.8! es funktioniert einfach nicht ^^
Alex

Antworten:


87

Ich würde nur einen benutzerdefinierten Filter erstellen. Sie sind nicht so schwer.

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

Vorlage:

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

Bearbeiten Sie hier den Link: Erstellen von Winkelfiltern

UPDATE : Hier ist eine Geige , die eine genaue Demo meines Vorschlags enthält.


Also gebe ich im Out die Filmobjekte zurück, die ich anzeigen möchte?
JustGoscha

Es tut mir leid, dass ich eine Weile gebraucht habe, um zurück zu kommen. Ich habe meine Antwort mit einem konkreten Beispiel aktualisiert.
Xesued

ok, danke, in der Zwischenzeit habe ich es selbst herausgefunden, aber es wird sicher später jemandem helfen :)
JustGoscha

Entweder habe ich zwei Werte wie aktiv und inaktiv. Wie kann ich dann mit diesem Filter separat suchen? Wir möchten keinen benutzerdefinierten Filter erstellen.
VjyV

80

Sie können eine Controller-Funktion zum Filtern verwenden.

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
Gibt es neben der Trennung von Bedenken einen Grund (in Bezug auf die Leistung), warum dies vermieden werden würde?
Sean Larkin

1
Javascript-Version, wenn andere danach suchen:$scope.filteredMovies = $filter('filter')($scope.movies, $scope.filterByGenres);
Evan

23

Das Erstellen eines benutzerdefinierten Filters kann hier zu viel des Guten sein. Sie können einfach einen benutzerdefinierten Komparator übergeben, wenn Sie über mehrere Werte verfügen:

$scope.selectedGenres = "Action, Drama"; 

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

dann im Filter:

filter:{name:selectedGenres}:containsComparator

1
Hat mir viel geholfen. Vielen Dank
Hristo Enev

@chrismarx Was wäre das ng-modelin der Ansichtsdatei, um mit dem beschriebenen Filter zu verknüpfen? (sorry, das Lernen noch hier) - Ich mag die Einfachheit Ihrer Antwort und versucht , es zu bekommen arbeiten , aber nicht sicher , wie meine beschriften <input>ist ng-model. :)
Twknab


18

Hier ist die Implementierung eines benutzerdefinierten Filters, der die Daten mithilfe eines Array von Werten filtert. Er unterstützt mehrere Schlüsselobjekte mit einem Array und einem einzelnen Wert von Schlüsseln. Wie bereits erwähnt, unterstützt der AngularJS-Filter inangularJS den Mehrfachschlüsselfilter mit einem einzigen Wert. Der unten stehende benutzerdefinierte Filter unterstützt jedoch dieselbe Funktion wie angleJS und unterstützt auch ein Array von Werten und eine Kombination aus Array und Einzelwert von Schlüsseln.

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

Verwendung:

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

Hier ist ein Geigenbeispiel mit der Implementierung des oben genannten benutzerdefinierten Filters "filterMutiple" . ::: Geigenbeispiel :::


1
Sie scheinen die richtige Absicht zu haben, aber Ihr Beispiel scheint nicht wie erwartet zu funktionieren. Die Ausgabe in der Tabelle scheint eher inkonsistent zu sein.
Hultner

Dies funktioniert nicht mit JSONs mit verschachtelten Objekten. Wäre ideal gewesen, wenn es so wäre. Außerdem wird ein length property undefinedFehler in der Konsole ausgelöst.
SinSync

Es funktioniert für mich, gibt jedoch alle Ergebnisse zurück, wenn das gesuchte Attribut in der Liste nicht gefunden wird. Wie kann ich dieses Verhalten invertieren und kein Ergebnis zurückgeben, wenn der Wert nicht in der Liste vorhanden ist?
Zakaria Belghiti

@ ZakariaBelghiti Wenn Sie keine Ergebnisse zurückgeben möchten, ändern Sie den Code: if (fData.length > 0){ this.filteredData = fData; } zu nur: this.filteredData = fData;
pconnor88

@Gerfried Ich denke, dass Site tatsächlich Stackoverflow kratzt, also haben sie seine Antwort gestohlen, nicht umgekehrt
Chris

13

Wenn Sie nach einem Array von Objekten filtern möchten, können Sie angeben

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

Einzelne Eigenschaften werden nach bestimmten Filtern gefiltert, die für diese Eigenschaft angegeben wurden.

Wenn Sie jedoch nach einzelnen Eigenschaften filtern und global nach allen Eigenschaften filtern möchten, können Sie so etwas tun.

<tr ng-repeat="supp in $data | filter : filterObject |  filter : search">
Wobei "filterObject" ein Objekt zum Durchsuchen einer einzelnen Eigenschaft ist und "Search" in jeder Eigenschaft global sucht.

~ Atul


Dies funktionierte gut für mich und war eine bessere Lösung als andere, die vorschlugen, die Filterung auf eine Funktion zu verlagern, da AngularJS beim Filtern nach Zeichenfolgen teilweise Textübereinstimmungen durchführt.
Snaver

9

Ich habe einige Zeit damit verbracht und dank @chrismarx habe ich gesehen, dass filterFilterSie mit der Standardeinstellung von Angular Ihren eigenen Komparator übergeben können. Hier ist der bearbeitete Komparator für mehrere Werte:

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

Und wenn wir einen benutzerdefinierten Filter für DRY erstellen möchten:

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

Und dann können wir es überall verwenden, wo wir wollen:

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

Zum Schluss noch ein Plunkr .

Hinweis: Das erwartete Array sollte nur einfache Objekte wie String , Number usw. enthalten.


Danke das hat mir geholfen. So suchen Sie nach einem Filter mit einem einzigen WertWithOr: {name: 'Jack'} Wenn jemand dasselbe braucht wie ich ...
Juan

7

Sie können den searchField-Filter von angle.filter verwenden

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
Sie können auch "where" von angle.filter <tr ng-repeat = "obj in collection | where: {id: 1}"> {{obj.name}} </ tr>
hcarreras

1
die searchHaltestellen hier.
Akash

1
Ich habe dies versucht und sowohl das Modul in meiner Winkel-App als auch das Skript in meinem Index-HTML eingerichtet, aber ich kann immer noch nach
ALLEN

7

Sie können auch verwenden, ngIfwenn die Situation dies zulässt:

<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
Schöne und einfache Lösung!
Leopoldo Sanczyk

7

Die schnellste Lösung, die ich gefunden habe, ist die Verwendung des filterByFilters aus dem Winkelfilter , zum Beispiel:

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

Der Vorteil ist , dass Winkel-Filter ist eine ziemlich beliebte Bibliothek (~ 2.6k Sterne auf GitHub) , die immer noch aktiv entwickelt und gepflegt wird , so ist es in Ordnung es zu einem Projekt als Abhängigkeit hinzuzufügen sein sollte.


1
Mit Abstand die beste Antwort. Vielen Dank.
Daniel Gruszczyk

4

Ich glaube, das ist es, wonach Sie suchen:

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

1
Es verkettet nur die Arrays und gibt keine Union zurück.
Muaaz

Wenn ich die Implementierung nicht falsch verstanden habe, sehe ich nicht, wie das ein Problem ist.
jKlaus

2

Bitte versuchen Sie dies

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

Nehmen wir an, Sie haben zwei Arrays, eines für Filme und eines für Genres

Verwenden Sie den Filter einfach als: filter:{genres: genres.type}

Hier haben Genres als Array und Typ einen Wert für das Genre


1

Ich habe dies für Zeichenfolgen UND Funktionalität geschrieben (ich weiß, dass es nicht die Frage ist, aber ich habe danach gesucht und bin hierher gekommen), vielleicht kann es erweitert werden.

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

Verwendung:

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


1

Ich hatte eine ähnliche Situation. Das Schreiben eines benutzerdefinierten Filters hat bei mir funktioniert. Hoffe das hilft!

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

Hier ist mein Beispiel, wie Filter und Direktiven für die Tabelle jsfiddle erstellt werden

Direktive Get List (Daten) und erstellen Sie eine Tabelle mit Filtern

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

Es ist mir ein Vergnügen, wenn ich Ihnen helfe



0

OPTION 1: Verwenden des von Angular bereitgestellten Filterkomparatorparameters

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

OPTION 2: Verwenden der von Angular bereitgestellten Filternegation

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

Meine Lösung

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

Auf VS 2015 überprüft, bekommt es nicht die Syntax und es funktioniert auch nicht
WholeLifeLearner

-15

Die beste Antwort ist:

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

5
Filtert dies nicht nur nach "Comedy"?
user1135469

2
Ich habe es versucht und es filtert nur nach "Comedy", nicht nach "Action".
Ben
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.