2012-12-14 8 views
24

Biorąc pod uwagę kontroler z właściwością $ scope, która jest obiektem z innymi właściwościami, a nie tablicą jak poniżej, jak mam odfiltrować zestaw ng-repeat?Filtrowanie na mapie obiektów zamiast tablic w AngularJS

Oto JSFiddle: http://jsfiddle.net/ZfGx4/110/

Kontroler:

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

Szablon:

<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 nie działa – Jackie

Odpowiedz

26

Zmieniłbym swoją strukturę danych na tablicę. Tak czy inaczej, oto kolejna implementacja do filtrowania obiektu znajomych.

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

ten iteruje obiektu tylko raz, porównuje przez name i phone i może być wywołana w ten sposób.

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

ja zdefiniował compareStr w innym module, ale tak naprawdę nie trzeba robić.

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

Nie zapomnij wstrzyknąć moduł filters do swojej aplikacji

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

Oto pełny przykład: http://jsbin.com/acagag/5/edit

+0

Świetne rozwiązanie, dzięki! –

13

Chyba nie można zrobić to bezpośrednio z 'filtr'. Patrząc na kod w angularjs, są to pierwsze linie z funkcji filtrowania:

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

Więc jeśli otrzyma coś innego z tablicy, to nic nie robi.

Oto jeden ze sposobów, aby to zrobić, nie wiem, czy polecam go, ale to jest pomysł:

W regulatorze, po prostu przekonwertować do tablicy przed przekazaniem go do filtra:

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

A ng powtarzania:

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

przykład: http://jsbin.com/acagag/2/edit

Może lepszym rozwiązaniem jest napisanie niestandardowego filtru.

+0

Znałem 'filter' filtracyjnych wymagane tablice, ale nie zrobił mi wiesz, że użyli 'instanceof Array', aby sprawdzić, czy coś jest tablicą! Spodziewałbym się innego rodzaju kontroli (http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/). W każdym razie, wziąłem twój przykład jako bazę dla mojego przykładu. – jaime

+0

+1 za powiedzenie DLACZEGO! Dzięki. – JnBrymn

1

zrobiłem przykład, w jaki sposób, aby nie zmieniać swój obiekt do tablicy . Myślę, że to odpowiedź na pytanie jest bardziej poprawne.

Miałem ten sam problem, że nie mogłem przeszukać obiektu.

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

Więc zamiast zwrócić tablicę wrócisz obiektu.

Mam nadzieję, że to pomoże komuś!

0

Zamiast stosując filtr można też po prostu:

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

I w szablonie

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

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

miałem ten sam problem, i udało się, tworząc swój własny filtr, który akceptuje mapę, przy jednoczesnym użyciu wbudowanego filtra do rzeczywistego dopasowania.

Mój filtr niestandardowy przechodzi przez mapę, a dla każdego elementu wywołuje wbudowany filtr, ale biorąc pod uwagę, że filtr akceptuje tylko tablicę, zawijam każdy element w tablicy o długości 1 (dane [poniżej]). Tak powiedzie mecz, jeśli długość w wyjściu z matrycą jest wciąż 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; 
    } 
}) 

Taka konfiguracja traci sprawność oczywiście dlatego, że wbudowany filtr ma obowiązek ustalić, co musi zrobić dla każdego elementu mapy, zamiast tylko raz, jeśli dasz mu tablicę zawierającą wszystkie elementy, ale w moim przypadku, z mapą 500 elementów, filtrowanie jest nadal natychmiastowe.

+0

to działa świetnie. dzięki –

1

Nie optymalne rozwiązanie, ale jeśli chcesz coś szybko i brudną:

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

Albo ng-if zamiast ng-hide

Powiązane problemy