2012-12-14 15 views
24

Dato un controller con una proprietà $ scope che è un oggetto con altre proprietà anziché una matrice come di seguito, come devo filtrare il set ng-repeat?Filtraggio sulla mappa di oggetti anziché in AngularJS

Ecco un JSFiddle: http://jsfiddle.net/ZfGx4/110/

Controller:

function HelloCntl($scope, $filter) { 
    $scope.friends = { 
     john: { 
      name: 'John', 
      phone: '555-1276' 
     }, 
     mary: { 
      name: 'Mary', 
      phone: '800-BIG-MARY' 
     }, 
     mike: { 
      name: 'Mike', 
      phone: '555-4321' 
     }, 
     adam: { 
      name: 'Adam', 
      phone: '555-5678' 
     }, 
     julie: { 
      name: 'Julie', 
      phone: '555-8765' 
     } 
    }; 
}​ 

Template:

<div ng:app> 
<div ng-controller="HelloCntl"> 
    <input placeholder="Type to filter" ng-model="query">  
    <ul> 
    <li ng-repeat="(id, friend) in friends | filter:query"> 
    <span>{{friend.name}} @ {{friend.phone}}</span> 
    </li> 
    </ul> 
</div> 
</div> 
+2

Jsfiddle non funziona più – Jackie

risposta

26

Vorrei cambiare la struttura dei dati in un array. Ad ogni modo, ecco un'altra implementazione per filtrare l'oggetto dei tuoi amici.

angular.module('filters',['utils']) 
    .filter('friendFilter', function(utils){ 

    return function(input, query){ 
     if(!query) return input; 
     var result = []; 

     angular.forEach(input, function(friend){ 
     if(utils.compareStr(friend.name, query) || 
      utils.compareStr(friend.phone, query)) 
      result.push(friend);   
     }); 
     return result; 
    }; 
    }); 

Questo itera sopra l'oggetto solo una volta, a confronto con name e phone e può essere chiamato così.

<li ng-repeat="friend in friends | friendFilter:query"> 

ho definito la compareStr in un altro modulo, ma non si ha realmente bisogno di farlo.

angular.module('utils', []) 
    .factory('utils', function(){ 
    return{ 
     compareStr: function(stra, strb){ 
     stra = ("" + stra).toLowerCase(); 
     strb = ("" + strb).toLowerCase(); 
     return stra.indexOf(strb) !== -1; 
     } 
    }; 
    }); 

Non dimenticare di iniettare il modulo di filters nella vostra applicazione

angular.module('app',['filters']) 

Ecco l'esempio completo: http://jsbin.com/acagag/5/edit

+0

Ottima soluzione, grazie! –

13

credo che non si può fare direttamente con 'filtro'. Guardando il codice di angular.js, queste sono le prime righe del funzione di filtro:

function filterFilter() { 
    return function(array, expression) { 
    if (!(array instanceof Array)) return array; 

Quindi, se si riceve qualcosa di diverso da un array, non fa nulla.

Ecco un modo per farlo, non so se lo consiglierei, ma è è un'idea:

Nel controllore, solo convertire in un array prima di passarlo al filtro:

$scope.filteredFriends = function() { 
    var array = []; 
    for(key in $scope.friends) { 
     array.push($scope.friends[key]); 
    } 
    return $filter('filter')(array, $scope.query); 
} 

E il ng-repeat:

<li ng-repeat="friend in filteredFriends()"> 

Esempio: http://jsbin.com/acagag/2/edit

Forse una soluzione migliore è scrivere un filtro personalizzato.

+0

sapevo il filtro richiesto array 'filter', ma io didn So che hanno usato 'instanceof Array' per verificare se qualcosa fosse un array! Mi sarei aspettato un diverso tipo di controllo (http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/). Ad ogni modo, ho preso il tuo esempio come base per il mio esempio. – jaime

+0

+1 per dire PERCHE '! Grazie. – JnBrymn

1

ho fatto un esempio su come non cambia l'oggetto a un array . Penso che questo risponda alla domanda più corretta.

Ho avuto lo stesso problema che non riuscivo a cercare in un oggetto.

http://jsbin.com/acagag/223/edit

angular.module('filters',['utils']) 
    .filter('friendFilter', function(utils){ 

    return function(input, query){ 
     if(!query) return input; 
     var result = {}; 

     angular.forEach(input, function(friendData, friend){ 
     if(utils.compareStr(friend, query) || 
      utils.compareStr(friendData.phone, query)) 
      result[friend] = friendData;   
     }); 
     return result; 
    }; 
    }); 

Quindi, solo invece di restituire un array che si restituire un oggetto.

Spero che questo aiuti qualcuno!

0

Invece di utilizzare un filtro si può anche semplicemente:

$http.get('api/users').success(function(data){ 
    angular.forEach(data, function(value, key){ 
     users.push(value); 
    }); 
    $scope.users = users; 
}); 

E nel modello

<input type="text" ng-model="query" placeholder="Search user"/> 

<li ng-repeat="user in users | filter:query"> 
    {{ user.name }} 
</li> 
3

Ho avuto lo stesso problema, e riuscii per creare il mio filtro che accetta una mappa, pur continuando a utilizzare il filtro incorporato per eseguire la corrispondenza effettiva.

Il mio filtro personalizzato attraversa la mappa, e per ogni elemento chiama il filtro incorporato, ma dato che quel filtro accetta solo una matrice, avvolgo ogni elemento in una matrice di lunghezza 1 (i [dati] sotto). Così la partita riesce se la lunghezza del l'uscita-array è ancora 1.

.filter('mapFilter', function($filter) { 
    var filter = $filter('filter'); 

    return function(map, expression, comparator) { 
    if (! expression) return map; 

    var result = {}; 
    angular.forEach(map, function(data, index) { 
     if (filter([data], expression, comparator).length) 
     result[index] = data;   
    }); 

    return result; 
    } 
}) 

Questa configurazione perde efficienza, naturalmente, perché il filtro incorporato è necessaria per determinare quello che deve fare per ogni elemento della mappa, invece di una sola volta se gli dai un array con tutti gli elementi, ma nel mio caso, con una mappa di 500 elementi il ​​filtraggio è ancora istantaneo.

+0

funziona alla grande. Grazie –

1

Non è una soluzione ottimale, ma se volete qualcosa di veloce e sporco:

<li ng-repeat="(id, friend) in friends | filter:query" ng-hide="id !== query.id"> 
    <span>{{friend.name}} @ {{friend.phone}}</span> 
</li> 

O ng-if invece di ng-hide

Problemi correlati