web-dev-qa-db-fra.com

Comment filtrer plusieurs valeurs (opération OU) dans angularJS

Je souhaite utiliser la variable filter dans angular et filtrer plusieurs valeurs. Si l'une de ces valeurs est définie, elle doit être affichée.

J'ai par exemple cette structure: 

Un objet movie qui a la propriété genres et que je veux filtrer pour Action et Comedy.

Je sais que je peux faire filter:({genres: 'Action'} || {genres: 'Comedy'}), mais que faire si je veux le filtrer de manière dynamique. Par exemple. filter: variableX

Comment définir variableX dans le $scope, lorsque j'ai un tableau des genres à filtrer?

Je pourrais le construire en tant que chaîne puis faire une eval() mais je ne veux pas utiliser eval () ...

128
JustGoscha

Je voudrais juste créer un filtre personnalisé. Ils ne sont pas si durs.

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

modèle:

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

Edit voici le lien: Création de filtres angulaires

UPDATE: Voici un violon qui a une démonstration exacte de ma suggestion.

83
Xesued

Vous pouvez utiliser une fonction de contrôleur pour filtrer.

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

Créer un filtre personnalisé est peut-être excessif ici, vous pouvez simplement passer un comparateur personnalisé, si vous avez des valeurs multiples telles que:

$scope.selectedGenres = "Action, Drama"; 

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

puis dans le filtre:

filter:{name:selectedGenres}:containsComparator
22
chrismarx

Voici l’implémentation du filtre personnalisé, qui filtrera les données à l’aide d’un tableau de valeurs. Il prendra en charge plusieurs objets de clé avec un tableau et une valeur unique de clés. Comme mentionné inangularJS API Filtre AngularJS Doc prend en charge le filtre à clés multiples avec une valeur unique, mais sous le filtre personnalisé prendra en charge la même fonctionnalité que angularJS et prend également en charge un tableau de valeurs et une combinaison de valeurs de tableau et de valeurs uniques. Veuillez trouver le code extrait ci-dessous,

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

Utilisation:

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

Voici un exemple de violon avec la mise en œuvre de "filterMutiple" filtre personnalisé . ::: Exemple de violon :::

17

Si vous souhaitez filtrer sur un tableau d'objets, vous pouvez donner

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

Une propriété individuelle sera filtrée par un filtre particulier donné pour cette propriété.

Mais si vous souhaitez utiliser quelque chose comme filtrer par propriété individuelle et filtrer globalement pour toutes les propriétés, vous pouvez procéder de la sorte.

<tr ng-repeat="supp in $data | filter : filterObject |  filter : search">
Où "filterObject" est un objet permettant de rechercher une propriété individuelle et "Rechercher" dans toutes les propriétés.

~ Atul

13
user1941574

J'ai passé un peu de temps dessus et grâce à @chrismarx, j'ai vu que la variable par défaut angular filterFilter vous permet de passer votre propre comparateur. Voici le comparateur édité pour plusieurs valeurs:

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

Et si nous voulons créer un filtre personnalisé pour 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);
      };
    });

Et ensuite, nous pouvons l'utiliser n'importe où:

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

Enfin, voici un plunkr .

Remarque: le tableau attendu ne doit contenir que des objets simples tels que String, Number etc.

9
s.alem

vous pouvez utiliser le filtre searchField de 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 -->
7
a8m

La solution la plus rapide que j'ai trouvée consiste à utiliser le filtre filterBy de angular-filter , par exemple:

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

L’avantage, c’est que angular-filter est une bibliothèque assez populaire (environ 2,6 000 étoiles sur GitHub), qui est toujours activement développée et maintenue. Il est donc conseillé de l’ajouter à votre projet en tant que dépendance.

6
Priidu Neemre

Vous pouvez également utiliser ngIf si la situation le permet:

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

Je crois que c'est ce que vous cherchez:

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

Supposons que vous avez deux tableaux, un pour le film et un pour le genre.

Utilisez simplement le filtre en tant que: filter:{genres: genres.type} 

Ici, les genres étant le tableau et le type a une valeur pour le genre

2
Amit Yadav

S'il vous plaît essayez ceci

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
rajeshpanwar

J'ai eu la même situation. L'écriture d'un filtre personnalisé a fonctionné pour moi. J'espère que cela t'aides!

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

Trop tard pour rejoindre le parti mais peut-être peut-il aider quelqu'un:

Nous pouvons le faire en deux étapes, d’abord filtrer par la première propriété puis concaténer par le deuxième filtre:

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

Voir le violon en marche avec filtre de propriétés multiples

1
Ali Adravi

J'ai écrit ceci pour les chaînes ET les fonctionnalités (je sais que ce n'est pas la question, mais je l'ai recherchée et je suis arrivé ici), peut-être qu'il peut être développé.

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

Usage:

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

Voici mon exemple comment create filter et directive for table jsfiddle

directive get list (datas) et crée une table avec des filtres

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

mon plaisir si je vous aide

1
Jasmin Kurtic

OPTION 1: Utilisation du paramètre comparateur de filtre fournisseur angulaire

// 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: Utilisation de la négation de filtre fournisseur angulaire

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