Фильтрация по нескольким конкретным свойствам модели в AngularJS (в отношении ИЛИ)


107

Взгляните на пример здесь: http://docs.angularjs.org/api/ng.filter:filter

Вы можете выполнять поиск по любому из свойств телефона, используя, <input ng-model="search">и вы можете искать только по имени, используя <input ng-model="search.name">, и результаты соответствующим образом фильтруются по имени (ввод номера телефона не возвращает никаких результатов, как ожидалось).

Скажем , у меня есть модель со свойством «имя», свойство «телефон», и «секретно» собственности, как бы я идти о фильтрации по как «имя» и «телефон» свойства и не «секрет» собственности ? По сути, пользователь может ввести имя или номер телефона, и ng-repeatфильтр будет правильно отфильтрован, но даже если пользователь ввел значение, равное части «секретного» значения, он ничего не вернет.

Спасибо.


Ха, я действительно сбит с толку, почему присоединение объекта «имя» к полю ввода ng-model(указание search.nameв поле INPUT ng-model) приведет к тому, что объекты, которые будут повторяться, будут отфильтрованы по их nameсвойству? То есть интуитивно для меня вы должны иметь возможность конкретно фильтровать, просто nameуказав в своем ng-repeatфильтре:, filter: friend.nameвместо `написания` <input ng-model = "search.name"> ...
LazerSharks

Ответы:


171

Вот плункер

Новый плункер с более чистым кодом и элементами списка поиска и запроса без учета регистра

Основная идея - создать функцию фильтра для достижения этой цели.

Из официального документа

функция: функция-предикат может использоваться для написания произвольных фильтров. Функция вызывается для каждого элемента массива. Конечный результат - это массив тех элементов, для которых предикат вернул истину.

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

Обновить

Некоторых людей может беспокоить производительность в реальном мире, и это правильно.

В реальном мире мы, вероятно, сделаем подобный фильтр из контроллера.

Вот подробный пост, показывающий, как это сделать.

Короче говоря, мы добавляем ng-changeво ввод для отслеживания новых изменений поиска

а затем запустить функцию фильтра.


1
Привет @maxisam, не могли бы вы объяснить, что означают двойные вертикальные линии? Я не совсем уверен, как читать эту строку:item.brand.indexOf($scope.query)!=-1 || item.model.indexOf($scope.query)!=-1)
LazerSharks

15
|| означает ИЛИ. Возможно, вы захотите прочитать Javascript, хорошие части.
maxisam

1
Кстати о ||, просто попробовал вот это, и он работает: <tr ng-repeat = "смартфон в смартфонах | filter: phone || name">. Первый фильтр имеет приоритет.
Jazzy

2
@kate angular.lowercase (item.brand) .indexOf ... в основном, все в нижнем регистре
maxisam

1
@maxisam. Строка запроса также должна быть в нижнем регистре, чтобы нечувствительность к регистру работала правильно. Вот вам вилка плункера с рабочей нечувствительностью к регистру. plnkr.co/edit/auz02bvWm1Vw4eItSa5J?p=preview Спасибо за фильтр.
Леопольд Кристьянссон

78

Вы можете передать объект в качестве параметра в выражение фильтра, как описано в Справочнике по API . Этот объект может выборочно применять интересующие вас свойства, например:

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

Вот плункер

Внимание ... этот пример отлично работает с AngularJS 1.1.5, но не всегда так хорошо с 1.0.7. В этом примере 1.0.7 инициализируется со всем отфильтрованным, а затем работает, когда вы начинаете использовать входы. Он ведет себя так, как будто входы содержат несовпадающие значения, даже если они начинаются пустыми. Если вы хотите оставаться на стабильных выпусках, попробуйте это в своей ситуации, но в некоторых сценариях может потребоваться использовать решение @ maxisam до выпуска 1.2.0.


26
Глядя на ваш плункер, как можно было бы достичь желаемого эффекта, если бы у вас было только одно поле поиска. Это проблема ... ИЛИ.
silvster27

2
Это золотой ответ, но он не отвечает на вопрос.
Кирилл ЧАПОН

1
Как бы вы это сделали с одним полем ввода, как в этом примере в таблице: datatables.net
Flash

Как бы вы подсчитали отфильтрованные данные в опубликованном Plunker?
Мухаммад Зишан Тахир

5

Я вдохновился ответом @ maxisam и создал свою собственную функцию сортировки, и я бы хотел поделиться ею (потому что мне скучно).

Ситуацию я хочу отфильтровать по массиву автомобилей. Выбранные свойства для фильтрации - это имя, год, цена и км. Цена собственности и км - это числа (отсюда и использование .toString). Я также хочу контролировать прописные буквы (следовательно .toLowerCase). Также я хочу иметь возможность разбить свой запрос фильтра на разные слова (например, учитывая фильтр 2006 Acura, он находит совпадения 2006 с годом и Acura с именем).

Функция я передаю фильтру

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

1
Не могли бы вы показать, как на стороне просмотра вы вызываете этот фильтр в Angular?
twknab



2

Здесь есть несколько хороших решений, но я бы предложил пойти другим путем. Если поиск - это самая важная вещь, а свойство secret вообще не используется в представлении; Мой подход к этому заключался бы в использовании встроенного углового фильтра со всеми его преимуществами и простом сопоставлении модели с новым массивом объектов.

Пример из вопроса:

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

Теперь в html просто используйте обычный фильтр для поиска обоих этих свойств.

<div ng-repeat="member in people | filter: searchString">

1
Мне нравится это решение для работы с небольшими объектами, но если у вас большой набор объектов с большим количеством вложенных вещей, отображение всего может сбивать с толку / легко сделать ошибку. +1 хотя для отличного решения для основных нужд ... если бы это не было так сложно отфильтровать только несколько свойств вместо всех! : / ... может быть, в будущем Angular сможет добавить некоторые встроенные функции для фильтров такого рода ...
twknab

2

Довольно простой подход с использованием filterBy в angularJs

Для работы plnkr перейдите по этой ссылке

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

Это специально использовало одно свойство для поиска по нескольким столбцам, но не по всем.

<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <script data-require="angular.js@1.4.1" data-semver="1.4.1" src="https://code.angularjs.org/1.4.1/angular.js"></script>
    <script data-require="angular-filter@0.5.2" 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>

контролер

// 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'
      }
      ];
  })

})();

Обратной стороной является добавление еще одного стороннего плагина. Важно отозвать.
mix3d 05

2

Я решил это просто:

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

1

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

Вот поиск с учетом регистра, который также разделяет ваш поиск на слова для поиска в каждой модели. Только когда он найдет пробел, он попытается разбить запрос на массив, а затем искать каждое слово.

Он возвращает истину, если каждое слово есть хотя бы в одной из моделей.

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

1

Фильтр может быть объектом JavaScript с полями, и вы можете иметь выражение как:

ng-repeat= 'item in list | filter :{property:value}'

0

Мне нравится, когда это возможно, просто. Мне нужно было сгруппировать по международному, отфильтровать по всем столбцам, отобразить количество для каждой группы и скрыть группу, если никаких элементов не существует.

Кроме того, я не хотел добавлять собственный фильтр только для таких простых вещей.

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

0

Я мог бы очень поздно, но это то, чем я закончил. Я сделал очень общий фильтр.

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

    })

Тогда используйте это так

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

Ваше окно поиска похоже

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

где name и device_id - это свойства в dvs


-1

Вот простое решение для тех, кому нужен быстрый фильтр по объекту:

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

Фильтр массива позволяет имитировать объект, который вы пытаетесь фильтровать. В приведенном выше случае следующие классы будут работать нормально:

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

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

И где может выглядеть колода:

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

  return {
    Cards: _cards
  };
};

А если вы хотите отфильтровать несколько свойств объекта, просто разделите имена полей запятой:

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

РЕДАКТИРОВАТЬ: Вот рабочий plnkr, который предоставляет пример одного и нескольких фильтров свойств:

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


Как бы вы реализовали этот тип поиска? В этом примере в их таблице вы можете ввести часть слова, нажать клавишу пробела и ввести часть другого слова в столбец, и он по-прежнему фильтрует соответственно. datatables.net
Flash

Я не знаю, почему мой пост был понижен в рейтинге, если я не ответил на вопрос должным образом. Код работает, если его правильно применить.
Рик

спасибо за ваш ответ Рик Я не знаю, кто проголосовал против, но я могу продолжить и проголосовать снова. Однако в этом примере, как это похоже на ссылку, которую я предоставил, где у них есть поле ввода для поиска и по мере ввода оно фильтруется. Это был мой оригинальный пост, за который кто-то проголосовал: stackoverflow.com/questions/42421941/angular-custom-search
Flash

1
Я обновил plnkr, чтобы показать, как может работать фильтр текстового поля. Обратите внимание, как он фильтрует любой фрагмент текста, как и ожидалось. Я считаю, что это ближе к вашему связанному примеру. Если вам нужно несколько фильтров (для отдельных частей данных), это другой вопрос.
Рик

@Rick Мне очень приятно, что вы потратили время на этот пример, так как в настоящее время я пытаюсь понять, что отфильтровываю только 2 свойства, а не все свойства в повторяющемся объекте ng-repeat. Однако ваш пример кажется немного сложным: есть ли способ его свести к минимуму? Я знаю, что вопрос, по сути, заключается в поиске одного поля поиска, которое может фильтровать только выбранные свойства. В вашем примере похоже, что мы фильтруем либо номиналы, либо числа, но не то и другое одновременно. Если я неправильно понимаю ваш пример, дайте мне знать!
twknab
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.