Filtrado por múltiples propiedades de modelo específicas en AngularJS (en relación OR)

107

Eche un vistazo al ejemplo aquí: http://docs.angularjs.org/api/ng.filter:filter

Puede buscar por cualquiera de las propiedades del teléfono usando <input ng-model="search">y puede buscar solo por el nombre usando <input ng-model="search.name">, y los resultados se filtran apropiadamente por nombre (escribir un número de teléfono no arroja ningún resultado, como se esperaba).

Digamos que tengo un modelo con una propiedad "nombre", una propiedad de "teléfono", y una propiedad "secreta", ¿cómo hago para filtrar por tanto el "nombre" y propiedades "teléfono" y no la propiedad "secreta" ? Entonces, en esencia, el usuario podría escribir un nombre o número de teléfono y el ng-repeatfiltro se filtraría correctamente, pero incluso si el usuario ingresara un valor que equivaliera a parte de un valor "secreto", no devolvería nada.

Gracias.

juguete de cuerda
fuente
Eh, estoy realmente confundido en cuanto a por qué adjuntar un objeto de "nombre" al campo de entrada ng-model(especificando search.nameen el campo de ENTRADA ng-model) daría como resultado que los objetos que se van a repetir sean filtrados por su namepropiedad. Es decir, intuitivamente para mí, debería poder filtrar específicamente con solo nameespecificar en su ng-repeatfiltro:, en filter: friend.namelugar de `escribir` <input ng-model = "search.name"> ...
LazerSharks

Respuestas:

171

Aquí está el plunker

Nuevo plunker con código más limpio y donde tanto la consulta como los elementos de la lista de búsqueda no distinguen entre mayúsculas y minúsculas

La idea principal es crear una función de filtro para lograr este propósito.

Del documento oficial

función: se puede utilizar una función de predicado para escribir filtros arbitrarios. La función se llama para cada elemento de la matriz. El resultado final es una matriz de esos elementos para los que el predicado devolvió verdadero.

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

Actualizar

Algunas personas pueden estar preocupadas por el rendimiento en el mundo real, lo cual es correcto.

En el mundo real, probablemente haríamos este tipo de filtro desde el controlador.

Aquí está la publicación detallada que muestra cómo hacerlo.

en resumen, agregamos ng-changea la entrada para monitorear el nuevo cambio de búsqueda

y luego activar la función de filtro.

maxisam
fuente
1
Hola @maxisam, ¿podrías explicar qué significan las líneas verticales dobles? No estoy realmente seguro de cómo leer esta línea:item.brand.indexOf($scope.query)!=-1 || item.model.indexOf($scope.query)!=-1)
LazerSharks
15
|| significa OR. Es posible que desee leer Javascript, las partes buenas.
maxisam
1
Hablando de ||, solo probé esto y funciona: <tr ng-repeat = "teléfono inteligente en teléfonos inteligentes | filtro: teléfono || nombre">. El primer filtro tiene prioridad.
Jazzy
2
@kate angular.lowercase (item.brand) .indexOf ... básicamente, solo en minúsculas todo
maxisam
1
@maxisam. La cadena de consulta también debe estar en minúsculas para que la insensibilidad a mayúsculas y minúsculas funcione correctamente. Aquí hay un plunker Fork of you con insensibilidad a mayúsculas y minúsculas. plnkr.co/edit/auz02bvWm1Vw4eItSa5J?p=preview Gracias por el filtro.
Leopold Kristjansson
78

Puede pasar un objeto como parámetro a su expresión de filtro, como se describe en la Referencia de API . Este objeto puede aplicar selectivamente las propiedades que le interesan, así:

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

Aquí está un Plunker

Atención ... este ejemplo funciona muy bien con AngularJS 1.1.5, pero no siempre tan bien en 1.0.7. En este ejemplo, 1.0.7 se inicializará con todo filtrado y luego funcionará cuando comience a usar las entradas. Se comporta como si las entradas tuvieran valores que no coinciden, aunque comienzan en blanco. Si desea permanecer en versiones estables, continúe y pruebe esto para su situación, pero es posible que algunos escenarios quieran usar la solución de @ maxisam hasta que se publique 1.2.0.

Anson
fuente
26
Mirando su plunker, ¿cómo se lograría el efecto deseado si solo tuviera un cuadro de búsqueda? Este es el desafío ... la parte del quirófano.
silvster27
2
Esta respuesta es oro, pero simplemente no responde a la pregunta.
Cyril CHAPON
1
¿Cómo lo haría con un solo campo de entrada como en este ejemplo en la tabla: datatables.net
Flash
¿Cómo obtendría el recuento de los datos filtrados en el Plunker publicado?
Muhammad Zeeshan Tahir
5

Me inspiré en la respuesta de @ maxisam y creé mi propia función de clasificación y pensé en compartirla (porque estoy aburrido).

Situación Quiero filtrar a través de una variedad de autos. Las propiedades seleccionadas para filtrar son nombre, año, precio y km. El precio de la propiedad y el km son números (de ahí el uso de .toString). También quiero controlar las letras mayúsculas (por lo tanto.toLowerCase ). También quiero poder dividir mi consulta de filtro en diferentes palabras (por ejemplo, dado el filtro Acura 2006, encuentra coincidencias 2006 con el año y Acura con el nombre).

Función paso a 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
fuente
1
¿Podría mostrar cómo, en el lado de la vista, llama a este filtro en Angular?
twknab
3

Espero que esta respuesta ayude, Filtro de valor múltiple

Y ejemplo de trabajo en este violín

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

Hay un montón de buenas soluciones aquí, pero sugeriría ir por la otra ruta con esto. Si la búsqueda es lo más importante y la propiedad 'secreta' no se usa en absoluto en la vista; mi enfoque para esto sería usar el filtro angular incorporado con todos sus beneficios y simplemente mapear el modelo a una nueva matriz de objetos.

Ejemplo de la pregunta:

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

Ahora, en html simplemente use el filtro regular para buscar ambas propiedades.

<div ng-repeat="member in people | filter: searchString">
Siddharth Singh
fuente
1
Me gusta esta solución para tratar con objetos pequeños, pero si tiene un gran conjunto de objetos con muchas cosas anidadas, mapear todo puede ser confuso / fácil de cometer un error. +1 aunque para una gran solución para necesidades básicas ... ¡Ojalá no fuera tan complicado filtrar solo unas pocas propiedades en lugar de todas! : / ... tal vez en el futuro Angular podría agregar alguna funcionalidad integrada para filtros de este tipo ...
twknab
2

Enfoque bastante sencillo usando filterBy en angularJs

Para trabajar plnkr siga este enlace

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

Esto ha utilizado especialmente una propiedad única para buscar en varias columnas, pero no en 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'
      }
      ];
  })

})();
CreíbleAshok
fuente
La desventaja es que agrega un complemento de terceros más. Importante llamar.
mix3d
2

Resolví esto simplemente:

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

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

Aquí hay una búsqueda sensible a mayúsculas y minúsculas que también separa su búsqueda en palabras para buscar también en cada modelo. Solo cuando encuentre un espacio, intentará dividir la consulta en una matriz y luego buscará cada palabra.

Devuelve verdadero si cada palabra está al menos en uno de los 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
fuente
1

El filtro puede ser un objeto JavaScript con campos y puede tener una expresión como:

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

Me gusta mantenerlo simple cuando sea posible. Necesitaba agrupar por Internacional, filtrar en todas las columnas, mostrar el recuento de cada grupo y ocultar el grupo si no existían elementos.

Además, no quería agregar un filtro personalizado solo por algo tan simple como esto.

        <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>
Luis Rebolloso
fuente
0

Puede que llegue muy tarde, pero esto es lo que terminé haciendo. Hice un filtro muy general.

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

    })

Entonces úsalo así

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

Tu cuadro de búsqueda es como

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

donde name y device_id son propiedades en dvs

Raj Sharma
fuente
-1

Aquí hay una solución simple para aquellos que desean un filtro rápido contra un objeto:

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

El filtro de matriz le permite imitar el objeto que está tratando de filtrar. En el caso anterior, las siguientes clases funcionarían bien:

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

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

Y dónde podría verse la plataforma:

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

  return {
    Cards: _cards
  };
};

Y si desea filtrar varias propiedades del objeto, simplemente separe los nombres de los campos con una coma:

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

EDITAR: Aquí hay un plnkr en funcionamiento que proporciona un ejemplo de filtros de propiedad únicos y múltiples:

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

Almiar
fuente
¿Cómo implementarías este tipo de búsqueda? En este ejemplo en su tabla, puede escribir parte de una palabra, presionar la tecla de espacio y escribir parte de otra palabra en una columna, y aún así se filtra en consecuencia. datatables.net
Flash
No sé por qué se degradó mi publicación a menos que no aborde la pregunta correctamente. El código funciona cuando lo aplica correctamente.
Rick
gracias por tu respuesta Rick No sé quién votó en contra, pero puedo seguir adelante y volver a votar. Sin embargo, en ese ejemplo, ¿en qué se parece al del enlace que proporcioné, donde tienen un campo de entrada de búsqueda y, a medida que escriben, se filtra? La suya era mi publicación original, alguien la había rechazado también: stackoverflow.com/questions/42421941/angular-custom-search
Flash
1
Actualicé el plnkr para mostrar cómo podría funcionar un filtro de cuadro de texto. Observe cómo se filtra en cualquier texto parcial como se esperaba. Creo que esto está más cerca de su ejemplo vinculado. Si necesita varios filtros (para datos separados), entonces esa es una pregunta diferente.
Rick
@Rick Realmente aprecio que dedique tiempo a este ejemplo, ya que actualmente estoy tratando de averiguar cómo filtrar solo 2 propiedades en lugar de que todas las propiedades dentro de un objeto se repitan a través de ng-repeat. Sin embargo, su ejemplo parece un poco complicado: ¿hay alguna forma de resumirlo? Sé que la pregunta es esencialmente buscar un único campo de búsqueda que pueda filtrar solo las propiedades seleccionadas. En su ejemplo, parece que estamos filtrando por nombres o números de valor nominal, pero no por ambos. Si no entiendo bien su ejemplo, hágamelo saber.
twknab