Filtrando por Múltiplas Propriedades Específicas de Modelo em AngularJS (no relacionamento OR)

107

Dê uma olhada no exemplo aqui: http://docs.angularjs.org/api/ng.filter:filter

Você pode pesquisar por qualquer uma das propriedades do telefone usando <input ng-model="search">e pode pesquisar apenas pelo nome usando <input ng-model="search.name">, e os resultados são filtrados apropriadamente por nome (digitar um número de telefone não retorna nenhum resultado, como esperado).

Digamos que eu tenho um modelo com uma propriedade "nome", uma propriedade "telefone", e uma propriedade "segredo", como eu iria sobre como filtrar por tanto o "nome" e as propriedades "Telefone" e não a propriedade "secreto" ? Então, em essência, o usuário poderia digitar um nome ou número de telefone e o ng-repeatfiltraria corretamente, mas mesmo se o usuário digitasse um valor que fosse igual a parte de um valor "secreto", ele não retornaria nada.

Obrigado.

brinquedo de vento
fonte
Huh, eu estou realmente confuso sobre por que anexar um objeto "nome" ao campo de entrada ng-model(especificando search.nameno campo INPUT ng-model) resultaria nos objetos a serem repetidos sendo filtrados por sua namepropriedade? Ou seja, intuitivamente para mim, você deve ser capaz de filtrar especificamente apenas nameespecificando em seu ng-repeatfiltro filter: friend.name:, em vez de `escrever` <input ng-model = "search.name"> ...
LazerSharks

Respostas:

171

Aqui está o êmbolo

Novo plunker com código mais limpo e em que os itens da lista de consulta e pesquisa não diferenciam maiúsculas de minúsculas

A ideia principal é criar uma função de filtro para atingir esse objetivo.

Do doc oficial

função: uma função de predicado pode ser usada para escrever filtros arbitrários. A função é chamada para cada elemento do array. O resultado final é uma matriz dos elementos para os quais o predicado retornou verdadeiro.

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

Atualizar

Algumas pessoas podem se preocupar com o desempenho no mundo real, o que é correto.

No mundo real, provavelmente faríamos esse tipo de filtro do controlador.

Aqui está a postagem detalhada mostrando como fazer isso.

em suma, adicionamos ng-changeà entrada para monitorar novas alterações de pesquisa

e então acionar a função de filtro.

maxisam
fonte
1
Olá @maxisam, pode explicar o que significam as linhas verticais duplas? Não tenho certeza de como ler esta linha:item.brand.indexOf($scope.query)!=-1 || item.model.indexOf($scope.query)!=-1)
LazerSharks
15
|| significa OU. Você pode querer ler Javascript, as partes boas.
maxisam
1
Por falar em ||, experimentei e funciona: <tr ng-repeat = "smartphone em smartphones | filtro: telefone || nome">. O primeiro filtro tem precedência.
Jazzy de
2
@kate angular.lowercase (item.brand) .indexOf ... basicamente, apenas minúsculas tudo
maxisam
1
@maxisam. A string de consulta também precisa estar em letras minúsculas para que a distinção entre maiúsculas e minúsculas funcione corretamente. Aqui está um Fork of you plunker com insensibilidade ao caso de trabalho. plnkr.co/edit/auz02bvWm1Vw4eItSa5J?p=preview Obrigado pelo filtro.
Leopold Kristjansson
78

Você pode passar um Object como parâmetro para sua expressão de filtro, conforme descrito na Referência da API . Esse objeto pode aplicar seletivamente as propriedades de seu interesse, como:

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

Aqui está um Plunker

Atenção ... este exemplo funciona muito bem com AngularJS 1.1.5, mas nem sempre tão bem em 1.0.7. Neste exemplo, 1.0.7 será inicializado com tudo filtrado e, em seguida, funcionará quando você começar a usar as entradas. Ele se comporta como se as entradas tivessem valores não correspondentes, embora comecem em branco. Se você deseja manter as versões estáveis, vá em frente e experimente isso para sua situação, mas alguns cenários podem querer usar a solução de @maxisam até o lançamento de 1.2.0.

Anson
fonte
26
Olhando para o seu êmbolo, como o efeito desejado seria alcançado se você tivesse apenas uma caixa de pesquisa. Este é o desafio ... a parte OR.
silvster27
2
Essa resposta é ouro, mas simplesmente não responde à pergunta.
Cyril CHAPON,
1
Como você faria isso com apenas um campo de entrada, como neste exemplo na tabela: datatables.net
Flash
Como você obteria a contagem dos dados filtrados no Plunker postado?
Muhammad Zeeshan Tahir
5

Eu me inspirei na resposta de @maxisam e criei minha própria função de classificação e pensei em compartilhá-la (porque estou entediado).

Situação que desejo filtrar em uma série de carros. As propriedades selecionadas para filtrar são nome, ano, preço e km. O preço da propriedade e km são números (daí o uso de .toString). Eu também quero controlar para letras maiúsculas (portanto .toLowerCase). Também quero ser capaz de dividir minha consulta de filtro em palavras diferentes (por exemplo, dado o filtro 2006 Acura, ele encontra correspondências 2006 com o ano e Acura com o nome).

Função que passo para filtrar

        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
fonte
1
Você poderia mostrar como, do lado da visualização, você chama esse filtro no Angular?
twknab de
5

Se você estiver aberto para usar bibliotecas de terceiros, 'Filtros angulares' com uma boa coleção de filtros pode ser útil:

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

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

Espero que esta resposta ajude, Multiple Value Filter

E um exemplo de trabalho neste violino

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

Há um monte de boas soluções aqui, mas eu sugiro seguir o outro caminho com isso. Se pesquisar é a coisa mais importante e a propriedade 'secreta' não é usada na visualização; minha abordagem para isso seria usar o filtro angular integrado com todos os seus benefícios e simplesmente mapear o modelo para uma nova matriz de objetos.

Exemplo da pergunta:

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

Agora, em html, basta usar o filtro regular para pesquisar essas propriedades.

<div ng-repeat="member in people | filter: searchString">
Siddharth Singh
fonte
1
Eu gosto dessa solução para lidar com objetos pequenos, mas se você tiver um grande conjunto de objetos com muitas coisas aninhadas, mapear tudo pode ser confuso / fácil de cometer um erro. +1 embora para uma ótima solução para necessidades básicas ... gostaria que não fosse tão complicado filtrar apenas algumas propriedades em vez de todas! : / ... talvez no futuro o Angular possa adicionar alguma funcionalidade integrada para filtros desse tipo ...
twknab
2

Abordagem bastante direta usando filterBy em angularJs

Para trabalhar com o plnkr, siga este link

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

Isso usou especialmente uma única propriedade para pesquisar várias colunas, mas não todas.

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

controlador

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

})();
CredibleAshok
fonte
A desvantagem é que adiciona mais um plugin de terceiros. É importante ressaltar.
mix3d
2

Resolvi isso simplesmente:

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

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

Aqui está uma pesquisa com distinção entre maiúsculas e minúsculas que também separa sua pesquisa em palavras para pesquisar em cada modelo. Somente quando encontrar um espaço, ele tentará dividir a consulta em um array e, em seguida, pesquisar cada palavra.

Ele retorna verdadeiro se todas as palavras estiverem em pelo menos um dos modelos.

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

O filtro pode ser um objeto JavaScript com campos e você pode ter uma expressão como:

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

Eu gosto de manter é simples quando possível. Eu precisava agrupar por Internacional, filtrar em todas as colunas, exibir a contagem para cada grupo e ocultar o grupo se nenhum item existisse.

Além disso, eu não queria adicionar um filtro personalizado apenas para algo simples como isso.

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

Posso chegar muito tarde, mas foi o que acabei fazendo. Fiz um filtro muito geral.

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

    })

Então use assim

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

Sua caixa de pesquisa seja como

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

onde name e device_id são propriedades em dvs

Raj Sharma
fonte
-1

Esta é uma solução simples para quem deseja um filtro rápido contra um objeto:

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

O filtro de matriz permite que você imite o objeto que está tentando filtrar. No caso acima, as seguintes classes funcionariam perfeitamente:

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

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

E onde o baralho pode ser:

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

  return {
    Cards: _cards
  };
};

E se você quiser filtrar várias propriedades do objeto, apenas separe os nomes dos campos com uma vírgula:

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

EDIT: Aqui está um plnkr de trabalho que fornece um exemplo de filtros de propriedade única e múltipla:

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

Rick
fonte
Como você implementaria esse tipo de pesquisa? Neste exemplo, em sua tabela, você pode digitar parte de uma palavra, pressionar a tecla de espaço e digitar parte de outra palavra em uma coluna, e ainda filtra de acordo. datatables.net
Flash
Não sei por que minha postagem foi rebaixada, a menos que não tenha respondido à pergunta de maneira adequada. O código funciona quando você o aplica corretamente.
Rick
Obrigado pela sua resposta Rick Não sei quem votou contra, mas posso ir em frente e votar novamente. No entanto, naquele exemplo, como ele é semelhante ao do link que forneci, onde eles têm um campo de entrada de pesquisa e, à medida que o digitam, ele é filtrado. A postagem dela era minha original. Someoune também votou nele: stackoverflow.com/questions/42421941/angular-custom-search
Flash
1
Eu atualizei o plnkr para mostrar como um filtro de caixa de texto pode funcionar. Observe como ele filtra qualquer texto parcial conforme o esperado. Acredito que isso esteja mais próximo do seu exemplo vinculado. Se você precisar de vários filtros (para dados separados), essa é uma questão diferente.
Rick
@Rick Eu realmente agradeço por você dedicar algum tempo a este exemplo, já que atualmente estou tentando me decidir a filtrar apenas 2 propriedades em vez de todas as propriedades dentro de um objeto que está sendo repetido via ng-repeat. No entanto, seu exemplo parece um pouco complicado: há alguma maneira de resumi-lo? Eu sei que a questão é essencialmente procurar um único campo de pesquisa que pode filtrar apenas por propriedades selecionadas. Em seu exemplo, parece que estamos filtrando por nomes de valor facial ou números, mas não ambos. Se eu não estiver entendendo seu exemplo, por favor me avise!
twknab de