Подтвердить что ты не робот

Фильтрация на карте объекта, а не массив в 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>
4b9b3361

Ответ 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>