web-dev-qa-db-fra.com

Filtrage par plusieurs propriétés de modèle spécifiques dans AngularJS (dans OR relation)

Jetez un coup d'œil à l'exemple ici: http://docs.angularjs.org/api/ng.filter:filter

Vous pouvez effectuer une recherche dans toutes les propriétés du téléphone en utilisant <input ng-model="search"> et vous pouvez rechercher uniquement le nom en utilisant <input ng-model="search.name">, et les résultats sont filtrés de manière appropriée par nom (la saisie d'un numéro de téléphone ne renvoie aucun résultat, comme prévu).

Disons que j'ai un modèle avec une propriété "name", une propriété "phone" et une propriété "secrète", comment puis-je filtrer par les propriétés "name" et "phone" et pas la propriété "secrète"? Donc, en gros, l'utilisateur peut taper un nom ou un numéro de téléphone et le ng-repeat devrait filtrer correctement, mais même s'il tapait une valeur équivalant à une partie d'une valeur "secrète", il ne renverrait rien.

Merci.

104
Matt Reyer

Voici le plunker

Nouveau plunker avec code plus propre et où les éléments de la liste de recherche et de la requête sont insensibles à la casse

L'idée principale est de créer une fonction de filtrage pour atteindre cet objectif.

De doc officiel

fonction: une fonction de prédicat peut être utilisée pour écrire des filtres arbitraires . La fonction est appelée pour chaque élément du tableau. Le résultat final est un tableau de ces éléments pour lesquels le prédicat a renvoyé la valeur true.

<input ng-model="query">

<tr ng-repeat="smartphone in smartphones | filter: search "> 

$scope.search = function(item) {
    if (!$scope.query || (item.brand.toLowerCase().indexOf($scope.query) != -1) || (item.model.toLowerCase().indexOf($scope.query.toLowerCase()) != -1) ){
        return true;
    }
    return false;
};

Mettre à jour

Certaines personnes pourraient être préoccupées par les performances dans le monde réel, ce qui est correct.

Dans le monde réel, nous ferions probablement ce type de filtre à partir du contrôleur.

Voici le détail post montrant comment le faire.

en bref, nous ajoutons ng-change à l'entrée pour surveiller le changement de recherche 

puis déclencher la fonction de filtre.

166
maxisam

Vous pouvez passer un objet en tant que paramètre de votre expression de filtre, comme décrit dans Référence de l'API . Cet objet peut appliquer sélectivement les propriétés qui vous intéressent, comme ceci:

<input ng-model="search.name">
<input ng-model="search.phone">
<input ng-model="search.secret">
<tr ng-repeat="user in users | filter:{name: search.name, phone: search.phone}">

Voici un Plunker

Heads up ... Cet exemple fonctionne très bien avec AngularJS 1.1.5, mais pas toujours aussi bien dans 1.0.7. Dans cet exemple, 1.0.7 sera initialisé avec tout ce qui est filtré, puis fonctionnera lorsque vous commencerez à utiliser les entrées. Il se comporte comme si les entrées contenaient des valeurs non correspondantes, même si elles étaient vides au début. Si vous souhaitez conserver des versions stables, essayez-le dans votre cas, mais certains scénarios peuvent utiliser la solution de @ maxisam jusqu'à la publication de la version 1.2.0.

76
Anson

Si vous êtes prêt à utiliser des bibliothèques tierces, il peut être utile d'utiliser des filtres angulaires avec une collection de filtres Nice:

https://github.com/a8m/angular-filter#filterby

collection | filterBy: [prop, nested.prop, etc..]: search
5
Khashayar

Je me suis inspiré de la réponse de @ maxisam pour créer ma propre fonction de tri et je pensais bien le partager (parce que je m'ennuie).

Situation Je veux filtrer à travers un tableau de voitures. Les propriétés sélectionnées à filtrer sont nom, année, prix et km. Le prix de la propriété et le kilomètre sont des nombres (d'où l'utilisation de .toString). Je veux aussi contrôler les lettres majuscules (d'où .toLowerCase). De plus, je souhaite pouvoir séparer ma requête de filtre en différents mots (par exemple, étant donné le filtre Acura 2006, il trouve les correspondances 2006 avec l'année et Acura avec le nom).

Fonction que je passe à filtrer

        var attrs = [car.name.toLowerCase(), car.year, car.price.toString(), car.km.toString()],
            filters = $scope.tableOpts.filter.toLowerCase().split(' '),
            isStringInArray = function (string, array){
                for (var j=0;j<array.length;j++){
                    if (array[j].indexOf(string)!==-1){return true;}
                }
                return false;
            };

        for (var i=0;i<filters.length;i++){
            if (!isStringInArray(filters[i], attrs)){return false;}
        }
        return true;
    };
5
NicolasMoise

J'espère que cette réponse vous aidera, Filtre de valeurs multiples

Et exemple de travail dans ce violon

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

Il y a un tas de bonnes solutions ici, mais je suggérerais d'aller de l'autre côté avec cela. Si la recherche est la chose la plus importante et que la propriété 'secret' n'est pas utilisée du tout dans la vue; Mon approche consiste à utiliser le filtre angulaire intégré avec tous ses avantages et à mapper simplement le modèle sur un nouveau tableau d'objets.

Exemple tiré de la question:

$scope.people = members.map(function(member) { 
                              return { 
                                name: member.name, 
                                phone: member.phone
                              }});

Maintenant, en HTML, utilisez simplement le filtre habituel pour rechercher ces deux propriétés.

<div ng-repeat="member in people | filter: searchString">
2
Siddharth Singh

Approche assez simple en utilisant filterBy in angularJs

Pour travailler plnkr suivez ce lien

http://plnkr.co/edit/US6xE4h0Gd5CEYV0aMDf?p=preview

Cela a spécialement utilisé une seule propriété pour rechercher plusieurs colonnes, mais pas toutes.

<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <script data-require="[email protected]" data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js"></script>
    <script data-require="[email protected]" data-semver="0.5.2" src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.2/angular-filter.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-controller="myCtrl as vm">
  <input type="text" placeholder="enter your search text" ng-model="vm.searchText" />
   <table>
     <tr ng-repeat="row in vm.tableData | filterBy: ['col1','col2']: vm.searchText">
       <td>{{row.col1}}</td>
       <td>{{row.col2}}</td>
       <td>{{row.col3}}</td>
     </tr>
   </table>
   <p>This will show filter from <b>two columns</b> only(col1 and col2) . Not from all. Whatever columns you add into filter array they
   will be searched. all columns will be used by default if you use <b>filter: vm.searchText</b></p>
  </body>
</html>

manette

// Code goes here
(function(){

  var myApp = angular.module('myApp',['angular.filter']);

  myApp.controller('myCtrl', function($scope){
    var vm= this;
    vm.x = 20;

    vm.tableData = [
      {
        col1: 'Ashok',
        col2: 'Tewatia',
        col3: '12'
      },{
        col1: 'Babita',
        col2: 'Malik',
        col3: '34'
      },{
        col1: 'Dinesh',
        col2: 'Tewatia',
        col3: '56'
      },{
        col1: 'Sabita',
        col2: 'Malik',
        col3: '78'
      }
      ];
  })

})();
1
CredibleAshok

http://plnkr.co/edit/A2IG03FLYnEYMpZ5RxEm?p=preview

Voici une recherche sensible à la casse qui sépare également votre recherche en mots à rechercher dans chaque modèle. Il ne tentera de scinder la requête en un tableau que s'il trouve un espace, puis de rechercher chaque mot. 

Il retourne vrai si chaque mot est au moins dans l'un des modèles. 

$scope.songSearch = function (row) {
    var query = angular.lowercase($scope.query);
    if (query.indexOf(" ") > 0) {
        query_array = query.split(" ");
        search_result = false;
        for (x in query_array) {
            query = query_array[x];
            if (angular.lowercase(row.model1).indexOf(query || '') !== -1 || angular.lowercase(row.model2).indexOf(query || '') !== -1 || angular.lowercase(row.model3).indexOf(query || '') !== -1){
                search_result = true;
            } else {
                search_result = false;
                break;
            }
        }
        return search_result;
    } else {
        return (angular.lowercase(row.model1).indexOf(query || '') !== -1 || angular.lowercase(row.model2).indexOf(query || '') !== -1 || angular.lowercase(row.model3).indexOf(query || '') !== -1);
    }
};
1
Tom Quinlan

J'ai résolu ceci simplement:

<div ng-repeat="Object in List | filter: (FilterObj.FilterProperty1 ? {'ObjectProperty1': FilterObj.FilterProperty1} : '') | filter:(FilterObj.FilterProperty2 ? {'ObjectProperty2': FilterObj.FilterProperty2} : '')">
0
Geovani Anholete

Le filtre peut être un objet JavaScript avec des champs et vous pouvez avoir une expression comme: 

ng-repeat= 'item in list | filter :{property:value}'
0
Gholamreza Fathpour

Voici une solution simple pour ceux qui veulent un filtre rapide contre un objet:

<select>
  <option ng-repeat="card in deck.Cards | filter: {Type: 'Face'}">{{card.Name}}</option>
</select>

Le filtre de tableau vous permet d'imiter l'objet que vous essayez de filtrer. Dans le cas ci-dessus, les classes suivantes fonctionneraient parfaitement:

var card = function(name, type) {
  var _name = name;
  var _type = type;

  return {
    Name: _name,
    Type: _type
  };
};

Et où le pont pourrait ressembler à:

var deck = function() {
  var _cards = [new card('Jack', 'Face'),
                new card('7', 'Numeral')];

  return {
    Cards: _cards
  };
};

Et si vous souhaitez filtrer plusieurs propriétés de l'objet, il suffit de séparer les noms de champs par une virgule:

<select>
  <option ng-repeat="card in deck.Cards | filter: {Type: 'Face', Name: 'Jack'}">{{card.Name}}</option>
</select>

EDIT: Voici un exemple de plnkr fonctionnel qui fournit un exemple de filtres de propriété simples et multiples:

http://embed.plnkr.co/i0wCx6l1WPYljk57SXzV/

0
Rick

J'aime garder c'est simple quand c'est possible. Je devais grouper par international, filtrer sur toutes les colonnes, afficher le nombre de chaque groupe et masquer le groupe s'il n'existait aucun élément.

De plus, je ne voulais pas ajouter de filtre personnalisé juste pour quelque chose de simple comme celui-ci.

        <tbody>
            <tr ng-show="fusa.length > 0"><td colspan="8"><h3>USA ({{fusa.length}})</h3></td></tr>
            <tr ng-repeat="t in fusa = (usa = (vm.assignmentLookups | filter: {isInternational: false}) | filter: vm.searchResultText)">
                <td>{{$index + 1}}</td>
                <td ng-bind-html="vm.highlight(t.title, vm.searchResultText)"></td>
                <td ng-bind-html="vm.highlight(t.genericName, vm.searchResultText)"></td>
                <td ng-bind-html="vm.highlight(t.mechanismsOfAction, vm.searchResultText)"></td>
                <td ng-bind-html="vm.highlight(t.diseaseStateIndication, vm.searchResultText)"></td>
                <td ng-bind-html="vm.highlight(t.assignedTo, vm.searchResultText)"></td>
                <td ng-bind-html="t.lastPublished | date:'medium'"></td>
            </tr>
        </tbody>
        <tbody>
            <tr ng-show="fint.length > 0"><td colspan="8"><h3>International ({{fint.length}})</h3></td></tr>
            <tr ng-repeat="t in fint = (int = (vm.assignmentLookups | filter: {isInternational: true}) | filter: vm.searchResultText)">
                <td>{{$index + 1}}</td>
                <td ng-bind-html="vm.highlight(t.title, vm.searchResultText)"></td>
                <td ng-bind-html="vm.highlight(t.genericName, vm.searchResultText)"></td>
                <td ng-bind-html="vm.highlight(t.mechanismsOfAction, vm.searchResultText)"></td>
                <td ng-bind-html="vm.highlight(t.diseaseStateIndication, vm.searchResultText)"></td>
                <td ng-bind-html="vm.highlight(t.assignedTo, vm.searchResultText)"></td>
                <td ng-bind-html="t.lastPublished | date:'medium'"></td>
            </tr>
        </tbody>
0
Louis Rebolloso

Je pourrais être très en retard mais c'est ce que j'ai fini par faire. J'ai fait un filtre très général.

angular.module('app.filters').filter('fieldFilter', function() {
        return function(input, clause, fields) {
            var out = [];
            if (clause && clause.query && clause.query.length > 0) {
                clause.query = String(clause.query).toLowerCase();
                angular.forEach(input, function(cp) {
                    for (var i = 0; i < fields.length; i++) {
                        var haystack = String(cp[fields[i]]).toLowerCase();
                        if (haystack.indexOf(clause.query) > -1) {
                            out.Push(cp);
                            break;
                        }
                    }
                })
            } else {
                angular.forEach(input, function(cp) {
                    out.Push(cp);
                })
            }
            return out;
        }

    })

Alors utilisez-le comme ça

<tr ng-repeat-start="dvs in devices |  fieldFilter:search:['name','device_id']"></tr>

Votre boîte de recherche soit comme

<input ng-model="search.query" class="form-control material-text-input" type="text">

où name et device_id sont des propriétés dans dvs

0
Raj Sharma