How does filter work in AngularJS?

All we need is an easy explanation of the problem, so here it is.

I have a table generated with ng-repeat (from an objects’ array).

I would like to filter it with a search text field.

Objects contained in my array has got deep properties.

I don’t know why and how, but the filter is only working on email field, which is as deep as other properties.

I’m using this search form :

<input type="text" name="search" ng-model="searchText" />
...
<tr ng-repeat="x in obj | filter:searchText track by $index">
  ...
</tr>

plunker

EDIT :

This answer helps me to understand why it’s not working.
Someone knows how I can bypass the $ verification in filter ?

I’m using $ because I’m following the Google Contact API format.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

You can check the source code of ngFilter here
It is set to ignore keys starting with $ as it’s a prefix used by AngularJS for public ($) and private ($$) properties.

Method 2

$ is a prefix used by Angular internal properties. For technical reasons, Angular prevents you to use it. Here is a workaround to deal with $ properties names without changing your JSON object:

You can iterate in ng-repeat over Object.keys($scope.object) instead $scope.object.

Demo on JSFiddle

Method 3

Since it is clear that we can change neither third party API nor AngularJS library code, we could go for modifying the object keys to not have $ in the beginning. But, since the data has so many of them at multiple level, let’s do it recursively! 🙂

Here’s how. I would remap each object in $scope.obj array to call a function:

$scope.obj = $scope.obj.map(function(cur) {
  return renameKey(cur)
})

Now, inside renameKey, it would check whether it’s an Array or Object using helper functions and call itself recursively while replacing the keys prepending x for the strings starting with $

function renameKey(cur) {
  if(isArray(cur)) {
    cur.forEach(function(obj) {
      obj = renameKey(obj)
    })
  } else if (isObject(cur)) {
    for (let key in cur) {
      if(key.charAt(0) === '$') {
        cur['x'+key] = cur[key];
        delete cur[key];
      }
      cur[key] = renameKey(cur[key])
    }
  } 
  return cur
}

function isObject(obj) {
   return obj && (typeof obj === "object");
}

function isArray(obj) { 
  return isObject(obj) && (obj instanceof Array);
}

Looks little tedius but it does work! Now, all we need to do is have x$t instead of $t in the HTML, and boom!

working plunker

Method 4

email works because nested property address doesn’t contain any $ char.

Unfortunately, I don’t think there is a way to bypass this behavior, however you can make your own filter and use it in ng-repeat.

This is simple example that should work for you:

JS

app.filter('customFilter', function() {
  return function(items, keyword) {
    if (!keyword || keyword.length === 0) return items;

    return items.filter(function(item){
      var phrase = keyword.$.toLowerCase();
      return item.gd$name.gd$fullName.$t.toLowerCase().includes(phrase) || 
        item.gd$name.gd$familyName.$t.toLowerCase().includes(phrase) || 
        item.gd$name.gd$givenName.$t.toLowerCase().includes(phrase) ||
        item.gd$email[0].address.toLowerCase().includes(phrase) ||
        item.gd$phoneNumber[0].$t.toLowerCase().includes(phrase) ||
        (!!item.gd$organization[0].gd$orgTitle && item.gd$organization[0].gd$orgTitle.$t.toLowerCase().includes(phrase)) ||
        (!!item.gd$organization[0].gd$orgName && item.gd$organization[0].gd$orgName.$t.toLowerCase().includes(phrase));
    });
  }
});

HTML

<tr ng-repeat="x in obj | customFilter:searchText">

Of course, you will have to add more checks for possible null values. I’ve just wanted to make it work on the data you’ve provided.

Hope, you’ll find it useful.

Here’s plunk

Method 5

I can’t comment because my reputation is less than 50 but as far as i can tell it’s any property that has a $ in it’s name is not used in the filter.. I tried changing the property names and this fixed the issue. Realise you may or may not have control over this.

Method 6

Suppose your obj is as below:

$scope.obj=[{firstName:'Jeet',lastName:'kumar'},{firstName:'test1',lastName:'dev'},{firstName:'test2',lastName:'other'}];

Search input box

<input type="text" name="search" ng-model="searchText" />

Datatable filter by index ‘firstName’

<tr ng-repeat="x in obj | filter:{firstName:searchText}">
 <td>{{x.firstName}}</td>
<td>{{x.lastName}}</td>
</tr>

Datatable filter over all index

<tr ng-repeat="x in obj | filter:searchText">
 <td>{{x.firstName}}</td>
<td>{{x.lastName}}</td>
</tr>

Desc:

filter:{name:searchText}
filter on the basis of 'firstName' index from your $socpe.obj array

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply