angularjssearchangular-filtersobject-comparisonor-condition

AngularJS - Filter by any of the properties


I've got an application where I show a list of items with AngularJS. I'm trying to make an easy search on this list but it searches on everything inside that item. An example of an item:

{
    id: 283727893,
    name: 'Item A',
    parent: {
        id: 239495838,
        name: 'Item B'
    },
    user: 'User C'
}

In the list I'm only writing the name of the root item, so parent.name does not appear.

The problem

However, if I search using an AngularJS filter by 'Item B', it still appears, because an inner property has this string.

What I've tried

I've put an object reference such as the following (where val is the text of the input):

vm.searchObj = {
    name: val,
    user: val
};

And then, on my DOM:

<li data-ng-repeat="[...] | filter: Ctrl.searchObj | orderBy: Ctrl.orderBy">
...
</li>

However, this works with conditional AND, so it only shows if both, name and user have val.

What I need

I'd like to take an input text and make a search to filter this list on given object properties (in plural).

So, in this example, this item should appear if I write 'Item A' or 'User C' (or just 'A' or just 'C' and so on), but not if I write 'B', because none of name and user have this letter.

I'd need it to take the reference object, in this case Ctrl.searchObj, and check if any of the given properties of the object contains val into the same structure of the filtered objects.

That, of course, should work into deeper levels of the object, so I could define the searchObj as follows and still be able to get if the filtered object has that structure inside:

vm.searchObj = {
    parent: {
        name: 'Item'
    }
};

In this case, it should show those items where its parent.name property contains the word 'Item'.

If you need more information, please, let me know!

Thank you!


Solution

  • If I understand correctly, an object being searched (we'll call it target) is a match if:

    The code below (which includes your edits) should do what you need. I'll leave it to you to fill in any edge cases (checks for hasOwnProperty, comparison of different types, etc.). I think filtering like this should only happen when necessary, like when a user types. Creating an Angular filter out of this could be too expensive, since it runs on each digest cycle:

    function objectContains(searchObj, target) {
        for (var property in searchObj) {
            var searchVal = searchObj[property];
            var targetVal = target[property];
    
            if (isObject(searchVal) && isObject(targetVal)) {
                if(objectContains(searchVal, targetVal)){
                    return true;
                }
            }
    
            if (
                !isObject(searchVal) && !isObject(targetVal) && 
                !isUndefinedOrNull(targetVal) && 
                !isUndefinedOrNull(searchVal) &&
                targetVal.toString().indexOf(searchVal.toString()) !== -1
            ) {
                return true;
            }
        }
    
        return false;
    }
    
    function isUndefinedOrNull(x) {
        return typeof x === "undefined" || x === null;
    }
    
    function isObject(x) {
        return typeof x === "object";
    }
    
    var filtered = list.filter(function(item) {
        return objectContains(searchObj, item);
    });