Daniel Pinkpank
Daniel Pinkpank

Reputation: 350

Multiple filter values in one input field (seperated by space) with AngularJS

I have an array of persons wich I display like that:

<div ng-repeat="person in persons | filterBy:filter">
  <div>{{person.name}}</div>
  <div>{{person.age}}</div>
  <div>{{person.city}}</div>
  <div>{{person.country}}</div>
</div>

My filter is bound to a text input field.

Now I would like to type multiple search criterias into my input field (seperated by space). Something like "peter 28 berlin". Now Angular should only display persons named "peter" at the age of "28" livin in "berlin".

Is there an easy way to achieve that?

Upvotes: 1

Views: 4291

Answers (2)

dfsq
dfsq

Reputation: 193291

You will have to write custom filter if you want this functionality. It's not really that complicated though.

var app = angular.module('filter', [])
app.controller('MainController', function($scope) {
    $scope.persons = [
        {name: 'Peter', age: 28, city: 'Berlin'},
        {name: 'Malik', age: 23, city: 'London'},
        {name: 'Sofia', age: 31, city: 'Paris'},
    ];
});


// filterBy implementation
app.filter('filterBy', function() {
    return function(array, query) {
    
        var parts = query && query.trim().split(/\s+/),
            keys = Object.keys(array[0]);
    
        if (!parts || !parts.length) return array;
    
        return array.filter(function(obj) {
            return parts.every(function(part) {
                return keys.some(function(key) {
                    return String(obj[key]).toLowerCase().indexOf(part.toLowerCase()) > -1;
                });
            });
        });
    };
});
.section {padding: 10px; margin-top: 10px; background: #EEE;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular.js"></script>
<div ng-app="filter" ng-controller="MainController">
    <input type="text" ng-model="filter">
    <div class="section" ng-repeat="person in persons | filterBy:filter">
        <div>{{person.name}}</div>
        <div>{{person.age}}</div>
        <div>{{person.city}}</div>
    </div>
</div>

Upvotes: 4

GPicazo
GPicazo

Reputation: 6676

It is not possibly (or at least not very easy) to do what you've asked since there needs to be some logic that determines what property on the object (name, age, city, country, ...) each word should match. However, you could enter into your input property:value pairs like: 'name:peter age:28 city:berlin' and use the following code (note, it is pseudo-code, so it may contains small errors):

<input ng-model="inputFilter"></input>

<div ng-repeat="person in persons | filter:myPersonFilter">
    <div>{{person.name}}</div>
    <div>{{person.age}}</div>
    <div>{{person.city}}</div>
    <div>{{person.country}}</div>
</div>

$scope.myPersonFilter(person) {
    if ($scope.inputFilter) {
        var filters = $scope.inputFilter.split(' ');
        var isMatch = true;

        filters.forEach(function(filter) {
            var pair = filter.split(':');

            if (pair.length === 2) {
                isMatch = (isMatch && (person[pair[0]] == pair[1]));
            }
        });

        return isMatch;
    }
};

Note that there are a lot of things to keep in mind though:

  • as you type, you will likely see a lot of jumping/flickering in your list unless you add some type of debounce to the input to throttle the filtering

  • The implementation above only does equal compare to the properties, so if you have a person named peter, then a filter of 'name:pet' will not work. Instead you may want to change the comparator to check for substring.

  • This implementation does not lend itself well to multiple filters on the same property. For example, it would not work well if you wanted to return all people who's name is Peter or whose name is Bob.

  • Usually, when you want to start filtering by multiple properties, it is better to build out a filtering interface instead of using a single input field.

  • If this is going to be a big feature of your app, you may want to consider moving the filtering to the app's server side (if one exists).

Upvotes: -1

Related Questions