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

107

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

Vous pouvez rechercher par n'importe laquelle des propriétés du téléphone à l'aide de <input ng-model="search">et vous pouvez rechercher uniquement par nom à l'aide de <input ng-model="search.name">, et les résultats sont correctement filtrés 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é "secret", comment procéder pour filtrer à la fois les propriétés "name" et "phone" et non la propriété "secret" ? Donc, en substance, l'utilisateur peut taper un nom ou un numéro de téléphone et leng-repeat filtrerait correctement, mais même si l'utilisateur tapait une valeur qui équivalait à une partie d'une valeur "secrète", il ne retournerait rien.

Merci.

winduptoy
la source
Huh, je suis vraiment confus quant à la raison pour laquelle attacher un objet «nom» au champ d'entrée ng-model(en spécifiant search.namedans le champ INPUT ng-model) entraînerait le filtrage des objets à répéter par leur namepropriété? Ie intuitivement pour moi, vous devriez être en mesure de filtrer spécifiquement nameen spécifiant simplement dans votre ng-repeatfiltre filter: friend.name:, au lieu de `écrire` <input ng-model = "search.name"> ...
LazerSharks

Réponses:

171

Voici le plunker

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

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

De la doc officielle

function: 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 des éléments pour lesquels le prédicat a renvoyé 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 peuvent avoir des inquiétudes sur les performances dans le monde réel, ce qui est correct.

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

Voici le post détaillé montrant comment le faire.

en bref, nous ajoutons ng-changeà l'entrée pour surveiller les nouveaux changements de recherche

puis déclenchez la fonction de filtre.

maxisam
la source
1
Salut @maxisam, pouvez-vous expliquer ce que signifient les doubles lignes verticales? Je ne sais pas vraiment comment lire cette ligne:item.brand.indexOf($scope.query)!=-1 || item.model.indexOf($scope.query)!=-1)
LazerSharks
15
|| signifie OU. Vous voudrez peut-être lire Javascript, les bonnes parties.
maxisam
1
En parlant de ||, je viens d'essayer ceci et cela fonctionne: <tr ng-repeat = "smartphone dans les smartphones | filter: phone || name">. Le premier filtre est prioritaire.
Jazzy
2
@kate angular.lowercase (item.brand) .indexOf ... fondamentalement, juste tout en minuscules
maxisam
1
@maxisam. La chaîne de requête doit également être en minuscules pour que l'insensibilité à la casse fonctionne correctement. Voici un Fork of you plunker avec une insensibilité au cas de travail. plnkr.co/edit/auz02bvWm1Vw4eItSa5J?p=preview Merci pour le filtre.
Leopold Kristjansson
78

Vous pouvez transmettre un objet en tant que paramètre à votre expression de filtre, comme décrit dans la référence API . Cet objet peut appliquer de manière sélective 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

Attention ... cet exemple fonctionne très bien avec AngularJS 1.1.5, mais pas toujours aussi bien dans 1.0.7. Dans cet exemple, la 1.0.7 s'initialise avec tout filtré, puis fonctionne lorsque vous commencez à utiliser les entrées. Il se comporte comme si les entrées contiennent des valeurs non correspondantes, même si elles commencent en blanc. Si vous voulez rester sur des versions stables, essayez ceci pour votre situation, mais certains scénarios peuvent vouloir utiliser la solution de @ maxisam jusqu'à ce que la version 1.2.0 soit publiée.

Anson
la source
26
En regardant votre plunker, comment l'effet souhaité serait-il obtenu si vous n'aviez qu'un seul champ de recherche. C'est le défi ... la partie OU.
silvster27
2
Cette réponse est en or mais ne répond tout simplement pas à la question.
Cyril CHAPON
1
Comment le feriez-vous avec un seul champ de saisie comme dans cet exemple dans le tableau: datatables.net
Flash
Comment obtiendriez-vous le décompte des données filtrées dans le Plunker publié?
Muhammad Zeeshan Tahir
5

Je me suis inspiré de la réponse de @ maxisam et j'ai créé ma propre fonction de tri et je pensais que je la partagerais (car je m'ennuie).

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

Fonction que je passe pour 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;
    };
NicolasMoise
la source
1
Pourriez-vous montrer comment, du côté de la vue, vous appelez ce filtre en Angular?
twknab
5

Si vous êtes prêt à utiliser des bibliothèques tierces, des 'filtres angulaires' avec une belle collection de filtres peuvent être utiles:

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

collection | filterBy: [prop, nested.prop, etc..]: search
Khashayar
la source
3

J'espère que cette réponse vous aidera, filtre à valeurs multiples

Et exemple de travail dans ce violon

arrayOfObjectswithKeys | filterMultiple:{key1:['value1','value2','value3',...etc],key2:'value4',key3:[value5,value6,...etc]}
Nirmal Kumar VeluPillai
la source
2

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

Exemple de la question:

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

Maintenant, en html, utilisez simplement le filtre régulier pour rechercher ces deux propriétés.

<div ng-repeat="member in people | filter: searchString">
Siddharth Singh
la source
1
J'aime cette solution pour traiter de petits objets, mais si vous avez un grand ensemble d'objets avec beaucoup de choses imbriquées, le mappage de tout peut être déroutant / facile de faire une erreur. +1 cependant pour une excellente solution pour les besoins de base ... je souhaite que ce ne soit pas si compliqué à filtrer pour seulement quelques propriétés au lieu de toutes! : / ... peut-être qu'à l'avenir, Angular pourrait ajouter des fonctionnalités intégrées pour des filtres de ce type ...
twknab
2

Approche assez simple utilisant filterBy dans 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'
      }
      ];
  })

})();
CrédibleAshok
la source
L'inconvénient est qu'il ajoute un autre plugin tiers. Important à appeler.
mix3d
2

J'ai résolu cela simplement:

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

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

Voici une recherche sensible à la casse qui sépare également votre recherche en mots pour rechercher également dans chaque modèle. Ce n'est que lorsqu'il trouve un espace qu'il essaiera de diviser la requête en un tableau, puis de rechercher chaque mot.

Il renvoie 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);
    }
};
Tom Quinlan
la source
1

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}'
Gholamreza Fathpour
la source
0

J'aime garder c'est simple quand c'est possible. J'avais besoin de regrouper par International, de filtrer sur toutes les colonnes, d'afficher le décompte de chaque groupe et de masquer le groupe si aucun élément n'existait.

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>
Louis Rebolloso
la source
0

Je suis peut-ê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 champ de recherche ressemble à

<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

Raj Sharma
la source
-1

Voici une solution simple pour ceux qui veulent un filtre rapide sur 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 très bien:

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

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

Et à quoi le deck 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, séparez simplement 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 plnkr fonctionnel qui fournit un exemple de filtres de propriétés uniques et multiples:

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

Meule
la source
Comment implémenteriez-vous ce type de recherche? Dans cet exemple, dans leur tableau, vous pouvez taper une partie d'un mot, appuyer sur la touche espace et taper une partie d'un autre mot dans une colonne, et il filtre toujours en conséquence. datatables.net
Flash le
Je ne sais pas pourquoi mon message a été rétrogradé à moins que je n'ai pas répondu correctement à la question. Le code fonctionne lorsque vous l'appliquez correctement.
Rick
merci pour votre réponse rick je ne sais pas qui a voté contre mais je peux aller de l'avant et voter de nouveau. Cependant, dans cet exemple, en quoi est-ce similaire à celui du lien que j'ai fourni où ils ont un champ d'entrée de recherche et au fur et à mesure qu'ils saisissent, il est filtré. C'était mon poste d'origine, quelqu'un l'avait également rejeté: stackoverflow.com/questions/42421941/angular-custom-search
Flash le
1
J'ai mis à jour le plnkr pour montrer comment un filtre de zone de texte peut fonctionner. Remarquez comment il filtre sur tout texte partiel comme prévu. Je pense que c'est plus proche de votre exemple lié. Si vous avez besoin de plusieurs filtres (pour des éléments de données séparés), c'est une question différente.
Rick
@Rick J'apprécie vraiment que vous consacriez du temps à cet exemple, car j'essaie actuellement de me résoudre à ne filtrer que 2 propriétés plutôt que toutes les propriétés d'un objet répétées via ng-repeat. Votre exemple semble cependant un peu compliqué: y a-t-il un moyen de le résumer? Je sais que la question recherche essentiellement un champ de recherche unique qui ne peut filtrer que pour les propriétés sélectionnées. Dans votre exemple, il semble que nous filtrons les noms de valeur faciale ou les nombres, mais pas les deux. Si je ne comprends pas bien votre exemple, faites-le moi savoir!
twknab