Filtro per più proprietà specifiche del modello in AngularJS (in relazione OR)


107

Dai un'occhiata all'esempio qui: http://docs.angularjs.org/api/ng.filter:filter

Puoi cercare in base a qualsiasi proprietà del telefono utilizzando <input ng-model="search">e puoi cercare solo in base al nome utilizzando <input ng-model="search.name">, ei risultati vengono opportunamente filtrati in base al nome (digitando un numero di telefono non viene restituito alcun risultato, come previsto).

Diciamo che ho un modello con una proprietà "nome", una proprietà "telefono", e una proprietà "segreto", come potrei fare per filtrare dal sia il "nome" e le proprietà "telefono" e non il "segreto" di proprietà ? Quindi, in sostanza, l'utente potrebbe digitare un nome o un numero di telefono e ng-repeatfiltrarebbe correttamente, ma anche se l'utente digitasse un valore che equivale a parte di un valore "segreto", non restituirebbe nulla.

Grazie.


Eh, sono davvero confuso sul motivo per cui allegare un oggetto "nome" al campo di input ng-model(specificando search.namenel campo INPUT ng-model) comporterebbe che gli oggetti vengano ripetuti filtrati dalla loro nameproprietà? Cioè per me intuitivamente, dovresti essere in grado di filtrare in modo specifico semplicemente namespecificando nel tuo ng-repeatfiltro filter: friend.name:, invece di `scrivere` <input ng-model = "search.name"> ...
LazerSharks

Risposte:


171

Ecco il plunker

Nuovo plunker con codice più pulito e in cui sia la query che gli elementi dell'elenco di ricerca non fanno distinzione tra maiuscole e minuscole

L'idea principale è creare una funzione di filtro per raggiungere questo scopo.

Da doc. Ufficiale

funzione: una funzione predicato può essere utilizzata per scrivere filtri arbitrari. La funzione viene chiamata per ogni elemento dell'array. Il risultato finale è un array di quegli elementi per i quali il predicato ha restituito 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;
};

Aggiornare

Alcune persone potrebbero avere dubbi sulle prestazioni nel mondo reale, il che è corretto.

Nel mondo reale, probabilmente faremmo questo tipo di filtro dal controller.

Ecco il post dettagliato che mostra come farlo.

in breve, aggiungiamo ng-changel'input per monitorare la nuova modifica della ricerca

e quindi attivare la funzione di filtro.


1
Ciao @maxisam, potresti spiegare cosa significano le doppie linee verticali? Non sono proprio sicuro di come leggere questa riga:item.brand.indexOf($scope.query)!=-1 || item.model.indexOf($scope.query)!=-1)
LazerSharks

15
|| significa OR. Potresti voler leggere Javascript, le parti buone.
maxisam

1
A proposito di ||, ho appena provato e funziona: <tr ng-repeat = "smartphone in smartphones | filter: phone || name">. Il primo filtro ha la precedenza.
Jazzy

2
@kate angular.lowercase (item.brand) .indexOf ... in pratica, tutto minuscolo
maxisam

1
@maxisam. Anche la stringa della query deve essere minuscola affinché l'insensibilità al maiuscolo / minuscolo funzioni correttamente. Ecco un Fork of you plunker con case insensitivity funzionante. plnkr.co/edit/auz02bvWm1Vw4eItSa5J?p=preview Grazie per il filtro.
Leopold Kristjansson

78

Puoi passare un oggetto come parametro all'espressione del filtro, come descritto nel riferimento API . Questo oggetto può applicare selettivamente le proprietà che ti interessano, in questo modo:

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

Ecco un Plunker

Attenzione ... questo esempio funziona alla grande con AngularJS 1.1.5, ma non sempre anche nella 1.0.7. In questo esempio 1.0.7 verrà inizializzato con tutto filtrato, quindi funzionerà quando inizi a utilizzare gli input. Si comporta come se gli input abbiano valori non corrispondenti, anche se iniziano vuoti. Se vuoi rimanere su versioni stabili, vai avanti e provalo per la tua situazione, ma alcuni scenari potrebbero voler utilizzare la soluzione di @ maxisam fino al rilascio della 1.2.0.


26
Guardando il tuo plunker, come si otterrebbe l'effetto desiderato se avessi solo una casella di ricerca. Questa è la sfida ... la parte OR.
silvster27

2
Questa risposta è oro ma semplicemente non risponde alla domanda.
Cyril CHAPON

1
Come lo faresti con un solo campo di input come in questo esempio nella tabella: datatables.net
Flash

Come otterresti il ​​conteggio dei dati filtrati nel Plunker pubblicato?
Muhammad Zeeshan Tahir,

5

Mi sono ispirato alla risposta di @maxisam e ho creato la mia funzione di ordinamento e avrei pensato di condividerlo (perché sono annoiato).

Situazione che voglio filtrare attraverso una serie di auto. Le proprietà selezionate da filtrare sono nome, anno, prezzo e km. Il prezzo della proprietà e i km sono numeri (da cui l'uso di .toString). Voglio anche controllare le lettere maiuscole (quindi .toLowerCase). Inoltre voglio essere in grado di suddividere la mia query di filtro in parole diverse (ad esempio, dato il filtro 2006 Acura, trova corrispondenze 2006 con l'anno e Acura con il nome).

Funzione passo a filtrare

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

1
Puoi mostrare come, dal lato della vista, chiami questo filtro in Angular?
twknab



2

Ci sono un sacco di buone soluzioni qui, ma suggerirei di seguire l'altra strada con questo. Se la ricerca è la cosa più importante e la proprietà "secret" non è affatto utilizzata nella vista; il mio approccio per questo sarebbe quello di utilizzare il filtro angolare integrato con tutti i suoi vantaggi e mappare semplicemente il modello su una nuova serie di oggetti.

Esempio dalla domanda:

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

Ora, in html usa semplicemente il filtro normale per cercare entrambe queste proprietà.

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

1
Mi piace questa soluzione per gestire piccoli oggetti, ma se hai un grande insieme di oggetti con molte cose annidate, mappare tutto può essere fonte di confusione / è facile commettere un errore. +1 però per un'ottima soluzione per le esigenze di base ... vorrei che non fosse così complicato filtrare solo per poche proprietà invece di tutte! : / ... forse in futuro Angular potrebbe aggiungere alcune funzionalità integrate per filtri di questo tipo ...
twknab

2

Approccio piuttosto diretto usando filterBy in angularJs

Per lavorare plnkr segui questo link

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

Questo ha usato specialmente una singola proprietà per cercare su più colonne ma non tutte.

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

  <head>
    <script data-require="angular.js@1.4.1" data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js"></script>
    <script data-require="angular-filter@0.5.2" 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>

controllore

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

})();

Lo svantaggio è che aggiunge un altro plug-in di terze parti. Importante da chiamare.
mix3d

2

L'ho risolto semplicemente:

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

1

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

Ecco una ricerca con distinzione tra maiuscole e minuscole che separa anche la ricerca in parole da cercare anche in ciascun modello. Solo quando trova uno spazio proverà a dividere la query in un array e quindi a cercare ogni parola.

Restituisce vero se ogni parola è almeno in uno dei modelli.

$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

Il filtro può essere un oggetto JavaScript con campi e puoi avere un'espressione come:

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

0

Mi piace mantenere la semplicità quando possibile. Avevo bisogno di raggruppare per Internazionale, filtrare su tutte le colonne, visualizzare il conteggio per ogni gruppo e nascondere il gruppo se non esistevano elementi.

Inoltre non volevo aggiungere un filtro personalizzato solo per qualcosa di semplice come questo.

        <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

Potrei essere molto in ritardo, ma questo è quello che ho finito per fare. Ho creato un filtro molto generale.

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

    })

Quindi usalo in questo modo

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

La tua casella di ricerca sarà come

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

dove name e device_id sono proprietà in dvs


-1

Ecco una semplice soluzione per coloro che desiderano un filtro rapido contro un oggetto:

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

Il filtro array ti consente di imitare l'oggetto che stai cercando di filtrare. Nel caso precedente, le seguenti classi funzionerebbero perfettamente:

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

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

E dove potrebbe apparire il mazzo:

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

  return {
    Cards: _cards
  };
};

E se vuoi filtrare più proprietà dell'oggetto, separa semplicemente i nomi dei campi con una virgola:

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

EDIT: ecco un plnkr funzionante che fornisce un esempio di filtri di proprietà singoli e multipli:

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


Come implementeresti questo tipo di ricerca? In questo esempio nella loro tabella puoi digitare parte di una parola, premere il tasto spazio e digitare parte di un'altra parola in una colonna, e filtra comunque di conseguenza. datatables.net
Flash

Non so perché il mio post è stato declassato a meno che non abbia risposto correttamente alla domanda. Il codice funziona quando lo applichi correttamente.
Rick

grazie per la tua risposta rick non so chi ha votato, ma posso andare avanti e votare di nuovo. Tuttavia, in quell'esempio è simile a quello nel link che ho fornito dove hanno un campo di input di ricerca e mentre lo digitano filtra. Anche il suo era il mio post originale, qualcuno l'aveva votato in basso: stackoverflow.com/questions/42421941/angular-custom-search
Flash

1
Ho aggiornato il plnkr per mostrare come potrebbe funzionare un filtro della casella di testo. Notare come filtra su qualsiasi testo parziale come previsto. Credo che questo sia più vicino al tuo esempio collegato. Se hai bisogno di più filtri (per parti di dati separate), questa è una domanda diversa.
Rick

@Rick Apprezzo molto che tu abbia dedicato del tempo a questo esempio, poiché attualmente sto cercando di capire da solo di filtrare solo 2 proprietà piuttosto che tutte le proprietà all'interno di un oggetto che vengono ripetute tramite ng-repeat. Il tuo esempio però sembra un po 'complicato: c'è un modo per ridurlo? So che la domanda è essenzialmente alla ricerca di un singolo campo di ricerca che può filtrare solo per le proprietà selezionate. Nel tuo esempio, sembra che stiamo filtrando per nomi o numeri del valore nominale, ma non per entrambi. Se non riesco a capire il tuo esempio, fammelo sapere!
twknab
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.