Фильтрация на карте объекта, а не массив в AngularJS
Для контроллера с свойством $scope, которое является объектом с другими свойствами, а не с массивом, как показано ниже, как следует фильтровать набор ng-repeat
?
Вот JSFiddle: http://jsfiddle.net/ZfGx4/110/
Контроллер:
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'
}
};
}
Шаблон:
<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>
Ответы
Ответ 1
Я бы изменил свою структуру данных на массив. Во всяком случае, здесь другая реализация, чтобы отфильтровать ваш объект друзей.
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;
};
});
Это повторяется по объекту только один раз, сравнивается с name
и phone
и может быть вызвано так.
<li ng-repeat="friend in friends | friendFilter:query">
Я определил compareStr
в другом модуле, но вам действительно не нужно это делать.
angular.module('utils', [])
.factory('utils', function(){
return{
compareStr: function(stra, strb){
stra = ("" + stra).toLowerCase();
strb = ("" + strb).toLowerCase();
return stra.indexOf(strb) !== -1;
}
};
});
Не забудьте ввести модуль filters
в ваше приложение
angular.module('app',['filters'])
Вот полный пример: http://jsbin.com/acagag/5/edit
Ответ 2
Я думаю, вы не можете сделать это напрямую с помощью "фильтра".
Рассматривая код в angular.js, это первые строки функции фильтра:
function filterFilter() {
return function(array, expression) {
if (!(array instanceof Array)) return array;
Итак, если он получает что-то отличное от массива, он ничего не делает.
Вот один из способов сделать это, не уверен, рекомендую ли я его, но это идея:
В контроллере просто преобразуйтесь в массив, прежде чем передавать его в фильтр:
$scope.filteredFriends = function() {
var array = [];
for(key in $scope.friends) {
array.push($scope.friends[key]);
}
return $filter('filter')(array, $scope.query);
}
И ng-repeat:
<li ng-repeat="friend in filteredFriends()">
Пример: http://jsbin.com/acagag/2/edit
Возможно, лучшим решением является создание настраиваемого фильтра.
Ответ 3
У меня была такая же проблема, и мне удалось создать собственный фильтр, который принимает карту, но при этом все еще использует встроенный фильтр для выполнения фактического соответствия.
Мой настраиваемый фильтр перемещается по карте, и для каждого элемента вызывается встроенный фильтр, но при условии, что этот фильтр принимает только массив, я обертываю каждый элемент в массив длиной 1 (ниже [данные]). Таким образом, совпадение выполняется успешно, если длина выходного массива все еще равна 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;
}
})
Эта установка теряет эффективность, потому что встроенный фильтр необходим для определения того, что ему нужно делать для каждого элемента на карте, а не только один раз, если вы дадите ему массив со всеми элементами, но в моем случае, с картой 500 элементов фильтрация все еще мгновенная.
Ответ 4
Я сделал пример о том, как не изменить свой объект на массив. Я думаю, что это отвечает на вопрос более правильным.
У меня была та же проблема, что я не мог выполнить поиск в объекте.
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;
};
});
Итак, вместо возврата массива вы возвращаете объект.
Надеюсь, это поможет кому-то!
Ответ 5
Не оптимальное решение, но если вы хотите что-то быстрое и грязное:
<li ng-repeat="(id, friend) in friends | filter:query" ng-hide="id !== query.id">
<span>{{friend.name}} @ {{friend.phone}}</span>
</li>
Или ng-if
вместо ng-hide
Ответ 6
Вместо использования фильтра вы также можете просто:
$http.get('api/users').success(function(data){
angular.forEach(data, function(value, key){
users.push(value);
});
$scope.users = users;
});
И в шаблоне
<input type="text" ng-model="query" placeholder="Search user"/>
<li ng-repeat="user in users | filter:query">
{{ user.name }}
</li>