Фільтрування за кількома специфічними властивостями моделі у 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замість того, щоб `
писати`

Відповіді:


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-repe = "смартфон у смартфонах | фільтр: телефон || ім'я">. Перший фільтр має перевагу.
Jazzy

2
@kate angular.lowercase (item.brand) .indexOf ... в основному, лише малі
регістри

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

Ось Plunker

Голова вгору ... цей приклад чудово працює з 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
Чи можете ви показати, як, з огляду, ви називаєте цей фільтр у кутовому?
twknab



2

Тут є маса хороших рішень, але я б радив піти іншим маршрутом з цим. Якщо пошук - це найважливіше, а властивість "секрету" взагалі не використовується для перегляду; мій підхід для цього полягав би в тому, щоб використовувати вбудований кутовий фільтр з усіма його перевагами та просто віднести модель до нового масиву об'єктів.

Приклад із запитання:

$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

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


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

де ім'я та device_id є властивостями у DVD-дисках


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

EDIT: Ось робочий plnkr, який надає приклад одиночних та декількох фільтрів властивостей:

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


Як би ви реалізували цей тип пошуку? У цьому прикладі в їхній таблиці ви можете ввести частину слова, натиснути клавішу пробілу та набрати частину іншого слова в стовпчик, і воно все одно фільтрується відповідно. datatables.net
Спалах

Я не знаю, чому моя посада була зменшена, якщо я не вирішив це питання належним чином. Код працює при правильному застосуванні.
Рік

дякую за вашу відповідь, я не знаю, хто проголосував, але я можу продовжувати і голосувати за нього. Однак у цьому прикладі, як це схоже на те, що у посиланні, яке я надав там, де у них є поле введення пошуку, і при їх введенні фільтрується. Її було моїм початковим повідомленням, якийсь також проголосував за неї: stackoverflow.com/questions/42421941/angular-custom-search
спалах

1
Я оновив plnkr, щоб показати, як може працювати фільтр текстового поля. Зверніть увагу, як він фільтрує будь-який частковий текст, як очікувалося. Я вважаю, що це ближче до вашого зв'язаного прикладу. Якщо вам потрібно кілька фільтрів (для окремих фрагментів даних), то це вже інше питання.
Рік

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