Come filtrare più valori (operazione OR) in angularJS


135

Voglio usare filterin angolare e voglio filtrare per più valori, se ha uno dei valori allora dovrebbe essere visualizzato.

Ho ad esempio questa struttura:

Un oggetto movieche ha la proprietà genrese voglio filtrare per Actione Comedy.

So di poterlo fare filter:({genres: 'Action'} || {genres: 'Comedy'}), ma cosa devo fare se voglio filtrarlo in modo dinamico. Per esempiofilter: variableX

Come faccio a impostare variableXin $scope, quando ho una matrice dei generi che devo filtrare?

Potrei costruirlo come una stringa e poi fare un eval()ma non voglio usare eval () ...


16
sei sicuro che funzioni filter:({genres: 'Action'} || {genres: 'Comedy'})anche? non entra angular 1.3.16.
twmulloy,

4
lo stesso per 1.4.8! semplicemente non funziona ^^
Alex,

Risposte:


87

Vorrei solo creare un filtro personalizzato. Non sono così difficili.

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

modello:

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

Modifica qui è il link: Creazione di filtri angolari

AGGIORNAMENTO : Ecco un violino che ha una dimostrazione esatta del mio suggerimento.


quindi in uscita restituisco gli oggetti del film che voglio visualizzare?
JustGoscha

Mi dispiace che ci sia voluto un po 'per tornare. Ho aggiornato la mia risposta con un violino di un esempio concreto.
Xesued

ok, grazie, nel frattempo l'ho capito da solo, ma sicuramente aiuterà qualcuno in seguito :)
JustGoscha

O ho due valori come attivo e inattivo, quindi come posso cercare separatamente usando questo filtro. non vogliamo creare filtri personalizzati.
VjyV,

80

È possibile utilizzare una funzione controller per filtrare.

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
Oltre alla separazione delle preoccupazioni, c'è qualche motivo (per quanto riguarda le prestazioni) che questo sarebbe evitato?
Sean Larkin,

1
versione javascript se altri lo stanno cercando:$scope.filteredMovies = $filter('filter')($scope.movies, $scope.filterByGenres);
Evan

23

La creazione di un filtro personalizzato potrebbe essere eccessiva qui, puoi semplicemente passare un comparatore personalizzato, se hai i valori multipli come:

$scope.selectedGenres = "Action, Drama"; 

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

quindi nel filtro:

filter:{name:selectedGenres}:containsComparator

1
Mi ha aiutato molto Grazie
Hristo Enev il

@chrismarx Quale sarebbe ng-modelnel file di visualizzazione da collegare al filtro descritto? (mi dispiace, ancora imparando qui) - Mi piace la semplicità della vostra risposta e cercando di farlo funzionare ma non è sicuro come etichettare i miei <input>'s ng-model. :)
twknab,


18

Ecco l'implementazione del filtro personalizzato, che filtrerà i dati utilizzando un array di valori e supporterà più oggetti chiave sia con array che con valore singolo di chiavi. Come accennato inangularJS API AngularJS filter Doc supporta il filtro a più chiavi con valore singolo, ma sotto il filtro personalizzato supporterà la stessa funzionalità di angularJS e supporta anche la matrice di valori e la combinazione di matrice e valore singolo delle chiavi. Trova lo snippet di codice di seguito,

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

Uso:

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

Ecco un esempio di violino con l'implementazione del filtro personalizzato "filterMutiple" sopra . ::: Esempio di violino :::


1
Sembra che tu abbia l'intenzione corretta, ma il tuo esempio non sembra funzionare come previsto. L'output nella tabella sembra piuttosto incoerente.
Hultner,

Questo non funziona con JSON con oggetti nidificati. Sarebbe stato l'ideale se lo avesse fatto. inoltre genera un length property undefinederrore nella console.
SinSync,

Funziona per me ma restituisce tutti i risultati se non trova l'attributo cercato nell'elenco, come posso fare per invertire questo comportamento e non restituire alcun risultato se il valore non esiste nell'elenco?
Zakaria Belghiti,

@ZakariaBelghiti se non si desidera restituire risultati, modificare il codice: if (fData.length > 0){ this.filteredData = fData; } in solo: this.filteredData = fData;
pconnor88

@Gerfried Penso che quel sito effettivamente gratta StackOverflow, quindi hanno rubato la sua risposta, non viceversa
Chris

13

Se vuoi filtrare su Array of Objects, allora puoi dare

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

Le singole proprietà verranno filtrate per filtro specifico fornito per quella proprietà.

Ma se volessi qualcosa come filtrare per singola proprietà e filtrare globalmente per tutte le proprietà, puoi fare qualcosa del genere.

<tr ng-repeat="supp in $data | filter : filterObject |  filter : search">
Dove "filterObject" è un oggetto per la ricerca di una singola proprietà e "Cerca" cercherà in ogni proprietà a livello globale.

~ Atul


Questo ha funzionato bene per me ed è stata una soluzione migliore rispetto ad altri, suggerendo di scaricare il filtro su una funzione poiché AngularJS esegue corrispondenze di testo parziali quando si filtra su stringhe.
Snaver,

9

Ci ho passato un po 'di tempo e grazie a @chrismarx ho visto che il valore predefinito di angular ti filterFilterconsente di passare il tuo comparatore. Ecco il comparatore modificato per più valori:

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

E se vogliamo creare un filtro personalizzato per 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);
      };
    });

E quindi possiamo usarlo dove vogliamo:

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

Finalmente ecco un plunkr .

Nota: l'array previsto dovrebbe contenere solo oggetti semplici come String , Number ecc.


Grazie questo mi ha aiutato. Per verificare la presenza di un filtro a valore singolo Con O: {nome: 'Jack'} Se qualcuno ha bisogno dello stesso di me ...
Juan

7

puoi usare il filtro searchField di 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
Puoi anche usare "where" di angular.filter <tr ng-repeat = "obj nella raccolta | dove: {id: 1}"> {{obj.name}} </tr>
hcarreras

1
le searchfermate qui.
Akash,

1
Ho provato questo e ho installato sia il modulo nella mia app angolare sia lo script nel mio indice HTML, ma sono ancora in grado di cercare TUTTI i campi, non solo quello che ho specificato :(
twknab,

7

Puoi anche usare ngIfse la situazione lo consente:

<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
Soluzione piacevole e semplice!
Leopoldo Sanczyk,

7

La soluzione più rapida che ho trovato è utilizzare il filterByfiltro dal filtro angolare , ad esempio:

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

Il lato positivo è che il filtro angolare è una libreria abbastanza popolare (~ 2.6k stelle su GitHub) che è ancora attivamente sviluppata e mantenuta, quindi dovrebbe andare bene aggiungerla al progetto come dipendenza.


1
Di gran lunga la migliore risposta. Grazie.
Daniel Gruszczyk,

4

Credo che questo sia quello che stai cercando:

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

1
Concatena le matrici e non restituisce un'unione.
muaaz,

A meno che non abbia frainteso l'implementazione, non riesco a vedere come sia un problema.
jKlaus,

2

Per favore, prova questo

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

Supponiamo che tu abbia due array, uno per il film e uno per il genere

Usa il filtro come: filter:{genres: genres.type}

Qui i generi essendo l'array e il tipo hanno valore per il genere


1

Ho scritto questo per stringhe E funzionalità (so che non è la domanda ma l'ho cercato e ottenuto qui), forse può essere espanso.

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

Uso:

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


1

Ho avuto una situazione simile. Scrivere filtri personalizzati ha funzionato per me. Spero che questo ti aiuti!

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

Ecco il mio esempio di come creare filtro e direttiva per table jsfiddle

direttiva ottiene l'elenco (dati) e crea una tabella con i filtri

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

piacere se ti aiuto


1

Troppo tardi per partecipare alla festa, ma potrebbe essere che possa aiutare qualcuno:

Possiamo farlo in due passaggi, primo filtro per prima proprietà e poi concatenato per secondo filtro:

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

Guarda il violino funzionante con più filtri proprietà


0

OPZIONE 1: utilizzo del parametro comparatore filtro angolare fornito

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

OPZIONE 2: Utilizzo della negazione del filtro fornito angolare

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

La mia soluzione

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

verificato su VS 2015, non ottiene la sintassi e non funziona anche
WholeLifeLearner

-15

la migliore risposta è:

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

5
Questo non filtra solo su "Commedia"?
user1135469

2
Ho provato questo e filtra solo su "Commedia", non su "Azione".
Ben,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.