Search code examples
javascripthtmlangularjsfilterangular-filters

Multi-Input Custom AngularJS Filter


I'm trying to build a custom Angular filter. In my example code, it's filtering a list of names. The filtering is done by a select dropdown with these options:

Contains
Equals
Starts with
Ends with
Does not contain

and then there is also a text field. The text field will act as a search field based on what's in the dropdown before it. Finally, when you click the 'filter' button, the filtering actually happens. So, if I enter 'foo' in the text field, then select 'Ends with' in the dropdown, then click 'filter', the list should only display entries that end in 'foo'.

You can see a codepen here.

Here's my HTML:

<div ng-app="programApp" ng-controller="programController">
  <label>Name: 
    <select ng-model="filterDropdown">
      <option>Contains</option>
      <option>Equals</option>
      <option>Starts with</option>
      <option>Ends with</option>
      <option>Does not contain</option>
    </select>
    <input ng-model="inputField" type="text">
  </label>
  <button ng-click="runFilter()">Filter</button>
  <ul>
    <li ng-repeat="name in names | filter:filterName(name, filterDropdown, filterSearch)">{{name.name}}, <i>{{name.age}}</i></li>
  </ul>
</div>

Then, here's the Angular filter (you can find more angular code in the codepen, but there isn't much else):

angular.module('programApp.filters', []).filter('filterName', function(){
    return function(entries, filterDropdown, filterInput) {
        //Each of these if statements should check which dropdown option is selected and return filtered elements accordingly.
        if(filterDropdown == 'Contains'){
            return entries.name.match(filterInput);
        }else if(filterDropdown == 'Equals'){
            return entries.name.match(filterInput);
        }else if(filterDropdown == 'Starts with'){
            if(entries.name.indexOf(filterInput) === 0){
                return entries;
            };
        }else if(filterDropdown == 'Ends with'){
            if(entries.name.indexOf(filterInput, entries.name.length - filterInput.length) !== -1){
                return entries;
            };
        }else if(filterDropdown == 'Does not contain'){
            return entries.name.match(!filterInput);
        };
    };
});

And here's my simple function that runs when you click 'filter' (meaning, when you type in text field, it doesn't apply the filter until you click the 'filter' button).

$scope.runFilter = function(){
  $scope.filterSearch = $scope.inputField;
};

However, when I run it, I don't get errors, but the filters don't actually do anything. What's wrong with my custom filter?

Codepen Here.


Solution

  • 1) Your HTML filter syntax is incorrect. You don't put filter at the beginning because that is already an existing Angular filter called filter. Just put the one you made. The arguments are then separated by colons, and the first argument is the entire array, but it is implied and you don't write it in.

    ng-repeat="thing in things | filterName:filterDropdown:filterSearch"
    

    2) You are trying to access the property of each entry, but you are writing entries.name. entries is your array, not each item, so you have to iterate over them. Angular has a nifty angular.forEach(array, function (item, index) {...}) which is meant exactly for this purpose.

    3) Your filter needs to return an array. Instantiate a new array at the beginning of your filter, iterate over your items using forEach and if the item passes a test, add it to the array, then return it.

    Codepen