Як фільтрувати кілька значень (АБО операція) у angularJS


135

Я хочу використовувати filteringular і хочу фільтрувати декілька значень, якщо у нього є одне із значень, то воно повинно відображатися.

У мене є, наприклад, така структура:

Об'єкт, movieякий має властивість, genresі я хочу фільтрувати для Actionта Comedy.

Я знаю, що можу зробити filter:({genres: 'Action'} || {genres: 'Comedy'}), але що робити, якщо я хочу його фільтрувати динамічно. Напрfilter: variableX

Як встановити variableXв $scope, коли у мене є масив жанрів , які я повинен фільтр?

Я міг би сконструювати його як рядок, а потім зробити, eval()але я не хочу використовувати eval () ...


16
ти впевнений, що filter:({genres: 'Action'} || {genres: 'Comedy'})навіть працює? він не входить angular 1.3.16.
twmulloy

4
те саме для 1.4.8! це просто не працює ^^
Олексій

Відповіді:


87

Я б просто створив спеціальний фільтр. Вони не такі важкі.

angular.module('myFilters', []).
  filter('bygenre', function() {
    return function(movies,genres) {
      var out = [];
      // Filter logic here, adding matches to the out var.
      return out;
    }
  });

шаблон:

<h1>Movies</h1>

<div ng-init="movies = [
          {title:'Man on the Moon', genre:'action'},
          {title:'Meet the Robinsons', genre:'family'},
          {title:'Sphere', genre:'action'}
       ];" />
<input type="checkbox" ng-model="genrefilters.action" />Action
<br />
<input type="checkbox" ng-model="genrefilters.family" />Family
<br />{{genrefilters.action}}::{{genrefilters.family}}
<ul>
    <li ng-repeat="movie in movies | bygenre:genrefilters">{{movie.title}}: {{movie.genre}}</li>
</ul>

Тут можна редагувати посилання: Створення кутових фільтрів

ОНОВЛЕННЯ : Ось загадка, яка має точну демонстрацію моєї пропозиції.


тож тоді на виході я повертаю об'єкти фільму, які я хочу показати?
JustGoscha

Вибачте, мені потрібно було деякий час повернутися. Я оновив свою відповідь скрипкою конкретного прикладу.
Xesued

ок, спасибі, тим часом я сам розібрався, але згодом хтось допоможе :)
JustGoscha

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

80

Для фільтрації можна використовувати функцію контролера.

function MoviesCtrl($scope) {

    $scope.movies = [{name:'Shrek', genre:'Comedy'},
                     {name:'Die Hard', genre:'Action'},
                     {name:'The Godfather', genre:'Drama'}];

    $scope.selectedGenres = ['Action','Drama'];

    $scope.filterByGenres = function(movie) {
        return ($scope.selectedGenres.indexOf(movie.genre) !== -1);
    };

}

HTML:

<div ng-controller="MoviesCtrl">
    <ul>
        <li ng-repeat="movie in movies | filter:filterByGenres">
            {{ movie.name }} {{ movie.genre }}
        </li>
    </ul>
</div>

1
Окрім відокремлення проблем, чи є якась причина (розумне виконання), щоб цього уникнути?
Шон Ларкін

1
версія javascript, якщо її шукають інші:$scope.filteredMovies = $filter('filter')($scope.movies, $scope.filterByGenres);
Evan

23

Створення спеціального фільтра може бути надмірним тут, ви можете просто передати спеціальний компаратор, якщо у вас є значення кратних, наприклад:

$scope.selectedGenres = "Action, Drama"; 

$scope.containsComparator = function(expected, actual){  
  return actual.indexOf(expected) > -1;
};

потім у фільтр:

filter:{name:selectedGenres}:containsComparator

1
Мені дуже допомагали. Спасибі
Христо Енев

@chrismarx Що буде ng-modelу файлі перегляду, щоб зв’язати описуваний фільтр? (Вибачте, все ще вчуся тут) - я як простота вашої відповіді і намагається отримати його роботу , але не знаєте , як позначати <input>«S ng-model. :)
twknab

вам потрібно прочитати це - docs.angularjs.org/api/ng/filter/filter
chrismarx

18

Ось реалізація спеціального фільтра, який буде фільтрувати дані за допомогою масиву значень. Він підтримуватиме декілька ключових об'єктів як з масивом, так і з одним значенням ключів. Як згадувалося в API ingulargularJS, AngularJS фільтр Doc підтримує кілька фільтрів ключів з одним значенням, але нижче спеціальний фільтр підтримуватиме таку ж функцію, що і angularJS, а також підтримує масив значень та комбінацію масиву та одного значення ключів. Знайдіть фрагмент коду нижче,

myApp.filter('filterMultiple',['$filter',function ($filter) {
return function (items, keyObj) {
    var filterObj = {
        data:items,
        filteredData:[],
        applyFilter : function(obj,key){
            var fData = [];
            if (this.filteredData.length == 0)
                this.filteredData = this.data;
            if (obj){
                var fObj = {};
                if (!angular.isArray(obj)){
                    fObj[key] = obj;
                    fData = fData.concat($filter('filter')(this.filteredData,fObj));
                } else if (angular.isArray(obj)){
                    if (obj.length > 0){
                        for (var i=0;i<obj.length;i++){
                            if (angular.isDefined(obj[i])){
                                fObj[key] = obj[i];
                                fData = fData.concat($filter('filter')(this.filteredData,fObj));    
                            }
                        }

                    }
                }
                if (fData.length > 0){
                    this.filteredData = fData;
                }
            }
        }
    };
    if (keyObj){
        angular.forEach(keyObj,function(obj,key){
            filterObj.applyFilter(obj,key);
        });
    }
    return filterObj.filteredData;
}
}]);

Використання:

arrayOfObjectswithKeys | filterMultiple:{key1:['value1','value2','value3',...etc],key2:'value4',key3:[value5,value6,...etc]}

Ось скрипковий приклад із реалізацією вищевказаного спеціального фільтра "filterMutiple" . ::: Приклад скрипки :::


1
Здається, ви маєте правильний намір, але ваш приклад, здається, не працює так, як очікувалося. Вихід у таблиці здається досить непослідовним.
Хультнер

Це не працює з JSON з вкладеними об'єктами. Було б ідеально, якби це було. також він видає length property undefinedпомилку в консолі.
SinSync

Він працює для мене, але він повертає всі результати, якщо він не знайде шуканий атрибут у списку, як я можу інвертувати цю поведінку і не повернути результату, якщо значення не існує в списку?
Zakaria Belghiti

@ZakariaBelghiti якщо ви не хочете повернути жодних результатів, то змініть код: if (fData.length > 0){ this.filteredData = fData; } просто: this.filteredData = fData;
pconnor88

@Gerfried Я думаю, що сайт насправді викреслює Stackoverflow, тому вони вкрали його відповідь, а не навпаки
Кріс

13

Якщо ви хочете відфільтрувати масив об'єктів, тоді ви можете дати

filter:({genres: 'Action', key :value }.

Індивідуальне майно буде фільтруватися спеціальним фільтром, заданим для цього ресурсу.

Але якщо ви хотіли зробити щось на кшталт фільтрування за окремими властивостями та глобального фільтрування за всіма властивостями, ви можете зробити щось подібне.

<tr ng-repeat="supp in $data | filter : filterObject |  filter : search">
Де "filterObject" є об'єктом для пошуку окремої власності, а "Search" здійснюватиме пошук у кожній власності в усьому світі.

~ Атул


Це добре працювало для мене і було кращим рішенням, ніж інші, пропонуючи вивантажувати фільтрацію до функції, оскільки AngularJS виконує часткові збіги тексту при фільтруванні на рядках.
Снайвер

9

Я витратив на це деякий час, і завдяки @chrismarx я побачив, що за замовчуванням у кутовому режимі filterFilterви можете пройти власний компаратор. Ось відредагований компаратор для кількох значень:

  function hasCustomToString(obj) {
        return angular.isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
  }
  var comparator = function (actual, expected) {
    if (angular.isUndefined(actual)) {
      // No substring matching against `undefined`
      return false;
    }
    if ((actual === null) || (expected === null)) {
      // No substring matching against `null`; only match against `null`
      return actual === expected;
    }
    // I edited this to check if not array
    if ((angular.isObject(expected) && !angular.isArray(expected)) || (angular.isObject(actual) && !hasCustomToString(actual))) {
      // Should not compare primitives against objects, unless they have custom `toString` method
      return false;
    }
    // This is where magic happens
    actual = angular.lowercase('' + actual);
    if (angular.isArray(expected)) {
      var match = false;
      expected.forEach(function (e) {
        e = angular.lowercase('' + e);
        if (actual.indexOf(e) !== -1) {
          match = true;
        }
      });
      return match;
    } else {
      expected = angular.lowercase('' + expected);
      return actual.indexOf(expected) !== -1;
    }
  };

І якщо ми хочемо зробити спеціальний фільтр для DRY:

angular.module('myApp')
    .filter('filterWithOr', function ($filter) {
      var comparator = function (actual, expected) {
        if (angular.isUndefined(actual)) {
          // No substring matching against `undefined`
          return false;
        }
        if ((actual === null) || (expected === null)) {
          // No substring matching against `null`; only match against `null`
          return actual === expected;
        }
        if ((angular.isObject(expected) && !angular.isArray(expected)) || (angular.isObject(actual) && !hasCustomToString(actual))) {
          // Should not compare primitives against objects, unless they have custom `toString` method
          return false;
        }
        console.log('ACTUAL EXPECTED')
        console.log(actual)
        console.log(expected)

        actual = angular.lowercase('' + actual);
        if (angular.isArray(expected)) {
          var match = false;
          expected.forEach(function (e) {
            console.log('forEach')
            console.log(e)
            e = angular.lowercase('' + e);
            if (actual.indexOf(e) !== -1) {
              match = true;
            }
          });
          return match;
        } else {
          expected = angular.lowercase('' + expected);
          return actual.indexOf(expected) !== -1;
        }
      };
      return function (array, expression) {
        return $filter('filter')(array, expression, comparator);
      };
    });

І тоді ми можемо використовувати його де завгодно:

$scope.list=[
  {name:'Jack Bauer'},
  {name:'Chuck Norris'},
  {name:'Superman'},
  {name:'Batman'},
  {name:'Spiderman'},
  {name:'Hulk'}
];


<ul>
  <li ng-repeat="item in list | filterWithOr:{name:['Jack','Chuck']}">
    {{item.name}}
  </li>
</ul>

Нарешті, ось плункр .

Примітка. Очікуваний масив повинен містити лише прості об'єкти, такі як String , Number і т.д.


Завдяки цьому мені допомогли. Щоб перевірити наявність фільтра з одним значеннямWithOr: {name: 'Jack'} Якщо комусь потрібно те саме, що і я ...
Juan

7

ви можете використовувати searchField фільтр angular.filter

JS:

$scope.users = [
 { first_name: 'Sharon', last_name: 'Melendez' },
 { first_name: 'Edmundo', last_name: 'Hepler' },
 { first_name: 'Marsha', last_name: 'Letourneau' }
];

HTML:

<input ng-model="search" placeholder="search by full name"/> 
<th ng-repeat="user in users | searchField: 'first_name': 'last_name' | filter: search">
  {{ user.first_name }} {{ user.last_name }}
</th>
<!-- so now you can search by full name -->

3
Ви також можете використовувати "де" для angular.filter <tr ng-repe = "obj у колекції | де: {id: 1}"> {{obj.name}} </tr>
hcarreras

1
searchзупиняється тут.
Акаш

1
Я спробував це і встановив і модуль у моєму кутовому додатку, і сценарій у моєму індексі HTML, але я все ще в змозі шукати ВСІ поля, а не лише те, що я вказав :(
twknab

7

Ви також можете використовувати, ngIfякщо ситуація дозволяє:

<div ng-repeat="p in [
 { name: 'Justin' }, 
 { name: 'Jimi' }, 
 { name: 'Bob' }
 ]" ng-if="['Jimi', 'Bob'].indexOf(e.name) > -1">
 {{ p.name }} is cool
</div>

1
Приємне і просте рішення!
Леопольдо Санчик

7

Найшвидше рішення, яке я знайшов, - це використовувати filterByфільтр з кутового фільтра , наприклад:

<input type="text" placeholder="Search by name or genre" ng-model="ctrl.search"/>   
<ul>
  <li ng-repeat="movie in ctrl.movies | filterBy: ['name', 'genre']: ctrl.search">
    {{movie.name}} ({{movie.genre}}) - {{movie.rating}}
  </li>
</ul>

Перевага полягає в тому, що кутовий фільтр - це досить популярна бібліотека (~ 2,6 тис. Зірок на GitHub), яка все ще активно розробляється та підтримується, тому слід добре додавати її до свого проекту як залежність.


1
На сьогодні найкраща відповідь. Дякую.
Даніель Грущик

4

Я вважаю, що це те, що ви шукаєте:

<div>{{ (collection | fitler1:args) + (collection | filter2:args) }}</div>

1
Він просто об'єднує масиви і не повертає об'єднання.
muaaz

Якщо я неправильно зрозумів реалізацію, я не розумію, як це проблема.
jKlaus

2

Спробуйте це

var m = angular.module('yourModuleName');
m.filter('advancefilter', ['$filter', function($filter){
    return function(data, text){
        var textArr = text.split(' ');
        angular.forEach(textArr, function(test){
            if(test){
                  data = $filter('filter')(data, test);
            }
        });
        return data;
    }
}]);

2

Припустимо, у вас є два масиви, один для фільму та один для жанру

Просто використовуйте фільтр як: filter:{genres: genres.type}

Тут жанри, що є масивом та типом, мають значення для жанру


1

Я написав це для рядків І функціональності (я знаю, це не питання, але я шукав його і потрапив сюди), можливо, його можна розширити.

String.prototype.contains = function(str) {
  return this.indexOf(str) != -1;
};

String.prototype.containsAll = function(strArray) {
  for (var i = 0; i < strArray.length; i++) {
    if (!this.contains(strArray[i])) {
      return false;
    }
  }
  return true;
}

app.filter('filterMultiple', function() {
  return function(items, filterDict) {
    return items.filter(function(item) {
      for (filterKey in filterDict) {
        if (filterDict[filterKey] instanceof Array) {
          if (!item[filterKey].containsAll(filterDict[filterKey])) {
            return false;
          }
        } else {
          if (!item[filterKey].contains(filterDict[filterKey])) {
            return false;
          }
        }
      }
      return true;
    });  
  };
});

Використання:

<li ng-repeat="x in array | filterMultiple:{key1: value1, key2:[value21, value22]}">{{x.name}}</li>


1

У мене була подібна ситуація. Написання спеціального фільтра працювало на мене. Сподіваюся, це допомагає!

JS:

App.filter('searchMovies', function() {
    return function (items, letter) {
        var resulsts = [];
        var itemMatch = new RegExp(letter, 'i');
        for (var i = 0; i < items.length; i++) {
            var item = items[i];
            if ( itemMatch.test(item.name) || itemMatch.test(item.genre)) {
                results.push(item);
            }
        }
        return results;
    };
});

HTML:

<div ng-controller="MoviesCtrl">
    <ul>
        <li ng-repeat="movie in movies | searchMovies:filterByGenres">
            {{ movie.name }} {{ movie.genre }}
        </li>
    </ul>
</div>

1

Ось мій приклад того, як створювати фільтр та директиву для таблиці jsfiddle

директива отримати список (дані) та створити таблицю з фільтрами

<div ng-app="autoDrops" ng-controller="HomeController">
<div class="row">
    <div class="col-md-12">
        <h1>{{title}}</h1>
        <ng-Multiselect array-List="datas"></ng-Multiselect>
    </div>
</div>
</div>

моє задоволення, якщо я допоможу тобі


1

Занадто пізно, щоб приєднатися до вечірки, але, можливо, це може допомогти комусь:

Ми можемо зробити це в два кроки, спочатку фільтруємо за першим властивістю, а потім об'єднуємо за другим фільтром:

$scope.filterd = $filter('filter')($scope.empList, { dept: "account" });
$scope.filterd = $scope.filterd.concat($filter('filter')($scope.empList, { dept: "sales" }));  

Дивіться робочу скрипку з декількома фільтрами властивостей


0

ВАРІАНТ 1: Використання параметру порівняльного фільтра, що надається кутом

// declaring a comparator method
$scope.filterBy = function(actual, expected) {
    return _.contains(expected, actual); // uses underscore library contains method
};

var employees = [{name: 'a'}, {name: 'b'}, {name: 'c'}, {name: 'd'}];

// filter employees with name matching with either 'a' or 'c'
var filteredEmployees = $filter('filter')(employees, {name: ['a','c']}, $scope.filterBy);

ВАРІАНТ 2: Використання кутового заперечення фільтра

var employees = [{name: 'a'}, {name: 'b'}, {name: 'c'}, {name: 'd'}];

// filter employees with name matching with either 'a' or 'c'
var filteredEmployees = $filter('filter')($filter('filter')(employees, {name: '!d'}), {name: '!b'});


-15

найкраща відповідь:

filter:({genres: 'Action', genres: 'Comedy'}

5
Це не просто фільтрує на «Комедії»?
користувач1135469

2
Я спробував це, і він фільтрується лише на "Комедія", а не на "Дія".
Бен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.