Filtrowanie według wielu specyficznych właściwości modelu w AngularJS (w relacji OR)


107

Spójrz na przykład tutaj: http://docs.angularjs.org/api/ng.filter:filter

Możesz wyszukiwać według dowolnych właściwości telefonu, używając <input ng-model="search">i możesz wyszukiwać tylko według nazwy, używając <input ng-model="search.name">, a wyniki są odpowiednio filtrowane według nazwy (wpisanie numeru telefonu nie zwraca żadnych wyników, zgodnie z oczekiwaniami).

Powiedzmy, że mam model z „name” właściwości, „telefon” własności, a „tajnego” własności, jak pójdę o filtrowaniu przez zarówno „Nazwa” i „Telefon” właściwości i nie z „tajne” i usługi ? W skrócie, użytkownik mógłby wpisać nazwę lub numer telefonu i ng-repeatfiltrowałby poprawnie, ale nawet jeśli użytkownik wpisałby wartość stanowiącą część „tajnej” wartości, nic nie zwróci.

Dzięki.


Huh, jestem naprawdę zdezorientowany, dlaczego dołączenie obiektu „nazwa” do pola wejściowego ng-model(określenie search.namew polu INPUT ng-model) miałoby spowodować, że obiekty, które mają być powtarzane, byłyby filtrowane według ich namewłaściwości? To znaczy intuicyjnie dla mnie, powinieneś być w stanie specjalnie filtrować, po prostu nameokreślając w swoim ng-repeatfiltrze: filter: friend.namezamiast `pisać` <input ng-model = "search.name"> ...
LazerSharks

Odpowiedzi:


171

Oto plunker

Nowy plunker z czystszym kodem, w którym zarówno w zapytaniach, jak i elementach listy wyszukiwania nie jest rozróżniana wielkość liter

Główną ideą jest stworzenie funkcji filtrującej, aby osiągnąć ten cel.

Z oficjalnego doc

funkcja: funkcja predykatu może służyć do pisania dowolnych filtrów. Funkcja jest wywoływana dla każdego elementu tablicy. Końcowym wynikiem jest tablica tych elementów, dla których predykat zwrócił prawdę.

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

Aktualizacja

Niektórzy ludzie mogą mieć obawy dotyczące wydajności w świecie rzeczywistym, co jest poprawne.

W prawdziwym świecie prawdopodobnie zrobilibyśmy tego rodzaju filtr ze sterownika.

Oto post szczegółowy pokazujący, jak to zrobić.

w skrócie, dodajemy ng-changedane wejściowe do monitorowania nowych zmian wyszukiwania

a następnie uruchom funkcję filtru.


1
Cześć @maxisam, czy możesz wyjaśnić, co oznaczają podwójne pionowe linie? Nie bardzo wiem, jak czytać tę linijkę:item.brand.indexOf($scope.query)!=-1 || item.model.indexOf($scope.query)!=-1)
LazerSharks

15
|| oznacza OR. Możesz przeczytać Javascript, dobre części.
maxisam

1
A propos ||, właśnie to wypróbowałem i działa: <tr ng-repeat = "smartfon w smartfonach | filter: phone || name">. Pierwszeństwo ma pierwszy filtr.
Jazzy

2
@kate angular.lowercase (item.brand) .indexOf ... w zasadzie tylko małe litery wszystko
maxisam

1
@maxisam. Ciąg zapytania również musi być zapisany małymi literami, aby funkcja niewrażliwości na wielkość liter działała poprawnie. Oto Fork of you plunker z niewrażliwością na wielkość liter. plnkr.co/edit/auz02bvWm1Vw4eItSa5J?p=preview Dzięki za filtr.
Leopold Kristjansson

78

Obiekt można przekazać jako parametr do wyrażenia filtru, zgodnie z opisem w dokumentacji interfejsu API . Ten obiekt może selektywnie zastosować interesujące Cię właściwości, na przykład:

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

Oto Plunker

Uwaga ... ten przykład działa świetnie z AngularJS 1.1.5, ale nie zawsze tak dobrze w 1.0.7. W tym przykładzie 1.0.7 zainicjuje się z odfiltrowaniem wszystkiego, a następnie zacznie działać, gdy zaczniesz używać danych wejściowych. Zachowuje się tak, jakby dane wejściowe zawierały niedopasowane wartości, mimo że na początku są puste. Jeśli chcesz pozostać przy stabilnych wydaniach, wypróbuj to w swojej sytuacji, ale niektóre scenariusze mogą chcieć używać rozwiązania @ maxisam do czasu wydania 1.2.0.


26
Patrząc na swojego plunkera, jak można osiągnąć pożądany efekt, gdybyś miał tylko jedno pole wyszukiwania. To jest wyzwanie ... część OR.
silvster27

2
Ta odpowiedź jest złota, ale po prostu nie odpowiada na pytanie.
Cyril CHAPON

1
Jak byś to zrobił z tylko jednym polem wejściowym, jak w tym przykładzie w tabeli: datatables.net
Flash,

Jak możesz policzyć przefiltrowane dane w opublikowanym Plunkerze?
Muhammad Zeeshan Tahir

5

Zainspirowałem się odpowiedzią @ maxisam i stworzyłem własną funkcję sortowania i myślałem, że się nią podzielę (bo się nudzę).

Sytuacja Chcę przefiltrować szereg samochodów. Wybrane właściwości do filtrowania to nazwa, rok, cena i km. Cena nieruchomości i km to liczby (stąd użycie .toString). Chcę również kontrolować wielkie litery (stąd .toLowerCase). Chciałbym również móc podzielić zapytanie filtru na różne słowa (np. Biorąc pod uwagę filtr 2006 Acura, wyszukuje on dopasowania 2006 z rokiem i Acura z nazwą).

Funkcja, którą przekazuję do filtra

        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
Czy możesz pokazać, jak po stronie widoku nazywasz ten filtr w Angular?
twknab



2

Jest tutaj kilka dobrych rozwiązań, ale proponuję wybrać inną drogę. Jeśli wyszukiwanie jest najważniejsze, a właściwość „sekret” nie jest w ogóle używana w widoku; moim podejściem byłoby użycie wbudowanego filtru kątowego ze wszystkimi jego zaletami i po prostu zmapowanie modelu do nowej tablicy obiektów.

Przykład z pytania:

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

Teraz w html po prostu użyj zwykłego filtru, aby przeszukać obie te właściwości.

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

1
Podoba mi się to rozwiązanie do obsługi małych obiektów, ale jeśli masz duży zestaw obiektów z dużą ilością zagnieżdżonych elementów, mapowanie wszystkiego może być mylące / łatwe do popełnienia błędu. Jednak +1 za świetne rozwiązanie dla podstawowych potrzeb ... szkoda, że ​​filtrowanie tylko dla kilku nieruchomości zamiast wszystkich nie jest tak skomplikowane! : / ... może w przyszłości Angular doda jakieś wbudowane funkcje dla filtrów tego typu ...
twknab

2

Całkiem proste podejście z użyciem filterBy w angularJs

Aby pracować plnkr, kliknij ten link

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

To specjalnie wykorzystało pojedynczą właściwość do wyszukiwania w wielu kolumnach, ale nie we wszystkich.

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

kontroler

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

})();

Wadą jest to, że dodaje jeszcze jedną wtyczkę innej firmy. Ważne, aby zadzwonić.
mix3d

2

Rozwiązałem to po prostu:

<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

Oto wyszukiwanie z rozróżnianiem wielkości liter, które również rozdziela Twoje wyszukiwanie na słowa, które mają być wyszukiwane również w każdym modelu. Dopiero gdy znajdzie spację, spróbuje podzielić zapytanie na tablicę, a następnie przeszukać każde słowo.

Zwraca prawdę, jeśli każde słowo znajduje się przynajmniej w jednym z modeli.

$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

Filtr może być obiektem JavaScript z polami i możesz mieć wyrażenie:

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

0

Lubię, gdy jest to możliwe, proste. Musiałem pogrupować według Międzynarodowe, przefiltrować wszystkie kolumny, wyświetlić liczbę dla każdej grupy i ukryć grupę, jeśli nie ma żadnych elementów.

Poza tym nie chciałem dodawać niestandardowego filtru tylko dla czegoś tak prostego.

        <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

Mogę się bardzo spóźnić, ale to właśnie zrobiłem. Zrobiłem bardzo ogólny filtr.

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

    })

Następnie użyj tego w ten sposób

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

Twoje pole wyszukiwania będzie podobne

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

gdzie name i device_id to właściwości w dvs


-1

Oto proste rozwiązanie dla tych, którzy chcą szybkiego filtrowania względem obiektu:

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

Filtr tablicy pozwala na naśladowanie obiektu, który próbujesz filtrować. W powyższym przypadku następujące klasy działałyby dobrze:

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

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

A jak mogłaby wyglądać talia:

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

  return {
    Cards: _cards
  };
};

A jeśli chcesz filtrować wiele właściwości obiektu, oddziel nazwy pól przecinkami:

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

EDYCJA: Oto działający plik plnkr, który zawiera przykład filtrów pojedynczych i wielu właściwości:

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


Jak zaimplementowałbyś tego typu wyszukiwanie? W tym przykładzie w ich tabeli możesz wpisać część słowa, nacisnąć klawisz spacji i wpisać część innego słowa w kolumnie, a nadal odpowiednio filtruje. datatables.net
Flash

Nie wiem, dlaczego mój post został zdegradowany, chyba że nie odpowiedziałem poprawnie na pytanie. Kod działa, gdy zastosujesz go poprawnie.
Rick

dziękuję za twoją odpowiedź rick Nie wiem, kto głosował przeciw, ale mogę kontynuować i zagłosować ponownie. Jednak w tym przykładzie, jak to jest podobne do tego w linku, który podałem, gdzie mają pole wejściowe wyszukiwania i gdy wpisują, filtruje. To był mój oryginalny post, który ktoś również odrzucił: stackoverflow.com/questions/42421941/angular-custom-search
Flash,

1
Zaktualizowałem plik plnkr, aby pokazać, jak może działać filtr pola tekstowego. Zwróć uwagę, jak zgodnie z oczekiwaniami filtruje on dowolny częściowy tekst. Uważam, że jest to bliższe połączonemu przykładowi. Jeśli potrzebujesz wielu filtrów (dla oddzielnych fragmentów danych), to jest inne pytanie.
Rick

@Rick Naprawdę doceniam, że poświęcasz czas na ten przykład, ponieważ obecnie próbuję dowiedzieć się, jak filtrować tylko 2 właściwości, a nie wszystkie właściwości obiektu, które są powtarzane za pomocą ng-repeat. Twój przykład wydaje się jednak nieco skomplikowany: czy można to w jakiś sposób sprowadzić? Wiem, że zasadniczo chodzi o szukanie pojedynczego pola wyszukiwania, które może filtrować tylko dla wybranych właściwości. W Twoim przykładzie wygląda na to, że filtrujemy według nazw wartości nominalnych lub liczb, ale nie obu. Jeśli nie rozumiem Twojego przykładu, daj mi znać!
twknab
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.