1

I have a simple ng-repeat like that:

<input type="text" ng-model="vm.filter">

<div ng-repeat="tenant in vm.tenants | filter : vm.filter : vm.contains">
 </div>

Now I want to filter tenants based on the value of the filter, e.g. to find if a name of a tenant contains the filter expression like that

function contains(actual, expected) {
            return actual.name.indexOf(expected) >= 0;
        }

What I do not understand is why I get a tenant.name in the contains function instead of the tenant itself. I know for a simple case I can do something like filter | {name: vm.filter} | vm.contains, but what if I want to do filtering based on multiple properties (e.g. name, phone, etc.)

3 Answers 3

1

What I do not understand is why I get a tenant.name in the contains function instead of the tenant itself.

What's happening is that angular is providing the comparator all levels of the object in an attempt to do a deep filtering i.e. it attempts to match your filter to all hierarchy levels in the object. If you console.log all the values, you'll see that you get the full object too (after all the properties).


One way to make this work would be something like

...
<div ng-repeat="tenant in vm.tenants | filter : vm.contains(vm.filter)">
...

and then

...
contains: function(filter) {
  return function(tenant) {
    return tenant.name.indexOf(filter) !== -1;
  }
}
...

Fiddle - https://jsfiddle.net/81384sd7/

Sign up to request clarification or add additional context in comments.

3 Comments

Yes, I figured that out by debugging that it is doing deep comparison. Is there a way to not do it? I want to have my custom logic in the provided function based on the object itself, but it seems not possible for some reason
You could put your filtering function as the 1st parameter to filter and directly use the vm.filter from the scope or do something like jsfiddle.net/u9rq7afk (type in abc to get the 2 matching tenants)
Yes, thanks that seems to do the trick, I have missed that the expression parameter can accept the function itself.
1

You can use an object to store the filters you want to apply. For example:

$scope.filterBy = {
  name: "Bob",
  phone: "07"
};

Then configure fields to edit the filters:

<input type="text" ng-model="filterBy.name" />
<input type="text" ng-model="filterBy.phone" />

Then just filter by this object.

<div ng-repeat="tenant in vm.tenants | filter : filterBy">

Take a look at my previous answer which is very similar except also allows selections to be made before a filter being applied manually with a button:

Applying a angularjs filter after "Apply" button click

I hope this helps.

5 Comments

While that will probably work if I create a specially crafted object with duplicate of my string, but I really want to have the same string matched against various properties of one object.
So you want to see if for example "Bob" exists in tenant.name, tenant.phone, etc? Essentially if it matches any property? If so, I believe that is the standard functionality if you just pass filter a string.
Yes, that's correct, but I don't want any, since there might be internal properties, properties that are not related, etc. That's why I just added my own function for that.
Thanks for help, I did not really understand that passing a filter string will do case insensitive contains for a property instead of equal, and then I can do e.g. this "user in users | filter:{name: search, phone: search}" to match it against multiple properties. I actually ended up with this simple solution, but the accepted answer was more to the point to the original problem :)
No problem, glad you got it sorted :).
0

You'll want to test for the presence of both those conditions and return the value if both are present. Something like this:

function contains(actual, expected) {
  if (actual.name.indexOf(expected) >= 0 && actual.phone.indexOf(expected) >= 0) {
    return actual;
  }
}

3 Comments

The problem is not that I cannot test for both inside the function, but that angular gives me properties in the object, instead of the object itself...
I corrected a mistake in the solution. It sounds like you need to return the entire actual object instead of the single property.
The actual object does not exist at all unfortunately. I would expect actual to be e.g. { name: "1", phone "2"}, instead I get "1" right away in the contains function

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.